Class ::xowf::WorkflowPage (public)

 ::xotcl::Class ::xowf::WorkflowPage[i]

Defined in

Testcases:
No testcase defined.
Source code:
namespace eval ::xowf {}
::nsf::object::alloc ::xotcl::Class ::xowf::WorkflowPage {set :__default_metaclass ::xotcl::Class
   set :__default_superclass ::xotcl::Object}
::xowf::WorkflowPage instproc create-or-use_view {-package_id:required -parent_id:required name} {
    # the link should be able to view return_url and template_file
    return [::$package_id returnredirect [::$package_id pretty_link -parent_id $parent_id $lang:$stripped_name]]
  }
::xowf::WorkflowPage instproc initialize_loaded_object {} {
    next
    #
    # Call "initialize" for workflows and workflow instances.  Before,
    # we called "initialize" only, when [:is_wf_instance] was true.
    #
    if {[:is_wf_instance] || [:is_wf]} {
      :initialize
    }
  }
::xowf::WorkflowPage instproc www-edit args {
    if {[:is_wf_instance]} {
      set ctx [::xowf::Context require [self]]
      set s [$ctx current_state]
      :include_header_info -css [$s extra_css] -js [$s extra_js]
    }
    next
  }
::xowf::WorkflowPage instproc get_anon_instances {} {
    if {[:istype ::xowiki::FormPage] && [:is_wf_instance]} {
      #
      # In case, the workflow definition has the autoname variable set,
      # it has the highest weight of all other sources.
      #
      set wfc [[::xowf::Context require [self]] wf_container]
      if {[$wfc exists autoname]} {
        return [$wfc set autoname]
      }
    }
    next
  }
::xowf::WorkflowPage instproc wfi_merged_form_constraints constraints_from_form {
    set ctx [::xowf::Context require [self]]
    set wf_specific_constraints [${:page_template} property form_constraints]
    set m [:merge_constraints $wf_specific_constraints  $constraints_from_form [$ctx get_form_constraints]]
    #:msg "merged:$m"
    return $m
  }
::xowf::WorkflowPage instproc get_revision_sets -with_instance_attributes:switch {
    set item_id ${:item_id}
    if {$with_instance_attributes} {
      set revision_sets [::xo::dc sets -prepare integer wf_revisions {
        SELECT revision_id, creation_date, last_modified, creation_user,
               creation_ip, state, assignee, instance_attributes
        FROM cr_revisions cr, acs_objects o, xowiki_form_page x, xowiki_page_instance pi
        WHERE cr.item_id = :item_id
        AND   o.object_id = cr.revision_id
        AND   x.xowiki_form_page_id = cr.revision_id
        AND   pi.page_instance_id = cr.revision_id
        ORDER BY cr.revision_id ASC
      }]
    } else {
      set revision_sets [::xo::dc sets -prepare integer wf_revisions {
        SELECT revision_id, creation_date, last_modified, creation_user, creation_ip, state, assignee
        FROM cr_revisions cr, acs_objects o, xowiki_form_page x
        WHERE cr.item_id = :item_id
        AND   o.object_id = cr.revision_id
        AND   x.xowiki_form_page_id = cr.revision_id
        ORDER BY cr.revision_id ASC
      }]
    }
    return $revision_sets
  }
::xowf::WorkflowPage instproc unset_temporary_instance_variables {} {
    # never save/cache the following variables
    unset -nocomplain :__wfi
    unset -nocomplain :__wf
    next
  }
::xowf::WorkflowPage instproc post_process_form_fields form_fields {
    #:log ------------------post_process_form_fields-feedback_mode=[info exists :__feedback_mode]
    if {[info exists :__feedback_mode]} {
      #
      # Provide feedback for every alternative
      #
      foreach f $form_fields {
        $f set_feedback ${:__feedback_mode}
      }
    }
    #:log ------------------post_process_form_fields-feedback_mode=[info exists :__feedback_mode]-DONE
  }
::xowf::WorkflowPage instproc render_form_action_buttons {{-formfieldButtonClass ::xowiki::formfield::submit_button} {-CSSclass ""}} {
    if {[:is_wf_instance]} {

      set ctx [::xowf::Context require [self]]
      set buttons {}
      foreach action [$ctx get_actions] {
        set success 0
        foreach role [$action roles] {
          set success [:check_role $role]
          if {$successbreak
        }
        if {$success} {
          set f [$formfieldButtonClass new  -name __action_[namespace tail $action]  -form_button_wrapper_CSSclass [$action wrapper_CSSclass]  -label_noquote [$action label_noquote]  -CSSclass $CSSclass  -destroy_on_cleanup  ]
          if {[$action extra_css_class] ne ""} {
            #$f append form_button_CSSclass " " [$action extra_css_class]
            $f form_button_CSSclass ""
            $f CSSclass_list_add form_button_CSSclass [$action extra_css_class]
          }
          $f CSSclass_list_add form_button_CSSclass prevent-double-click
          #ns_log notice "RENDER BUTTON has CSSclass [$f CSSclass] // [$f form_button_CSSclass]"
          if {[$action exists title]} {
            $f title [$action title]
          }
          $f value [$action label]
          lappend buttons $f
        }
      }
      #
      # Render the button widgets.
      #
      :render_form_action_buttons_widgets -CSSclass $CSSclass $buttons
    } else {
      next
    }
  }
::xowf::WorkflowPage instproc render_icon {} {
    if {[:info procs render_icon] ne ""} {
      #
      # In case, we have a per-object method (i.e., defined via the
      # workflow), use this with highest precedence.
      #
      next

    } elseif {[:is_wf_instance]} {
      set page_template ${:page_template}
      set title [::$page_template title]
      regsub {[.]wf$} $title "" title
      return [list text $title is_richtext false]
    } elseif {[:is_wf]} {
      return [list text "Workflow" is_richtext false]
    } else {
      next
    }
  }
::xowf::WorkflowPage instproc wf_property {name {default {}}} {
    if {[info exists :__wf]} {set key :__wf($name)} else {set key :__wfi($name)}
    if {[info exists $key]} { return [set $key] }
    return $default
  }
::xowf::WorkflowPage instproc call_action {-action {-attributes {}}} {
    if {![:is_wf_instance]} {
      error "Page [self] is not a Workflow Instance"
    }

    set actionObj [:get_action_obj -action $action]
    return [$actionObj invoke -attributes $attributes]
  }
::xowf::WorkflowPage instproc send_to_assignee {-subject -from -body {-mime_type text/plain} {-with_ical:boolean false}} {
    set wf_name [${:page_template} name]

    if {![info exists subject]} {
      set subject "\[$wf_name\] ${:title} (${:state})"
    }
    if {![info exists :from]} {set from ${:creation_user}}
    acs_user::get -user_id ${:assignee} -array to_info
    acs_user::get -user_id $from -array from_info

    set message_id [mime::uniqueID]
    set message_date [acs_mail_lite::utils::build_date]
    set tokens [mime::initialize  -canonical $mime_type  -encoding "quoted-printable" -string $body]

    if {$with_ical} {
      set items [::xo::OrderedComposite new -destroy_on_cleanup -mixin ::xo::ical::VCALENDAR]
      # hmm, mozilla just supports VEVENT, a VTODO would be nice.
      $items add [::xo::ical::VEVENT new  -creation_date ${:creation_date}  -last_modified ${:last_modified}  -dtstart "now"  -uid ${:package_id}-${:revision_id}  -url [:pretty_link -absolute true]  -summary $subject  -description "Workflow instance of workflow $wf_name ${:description}"]
      $items configure -prodid "-//WU Wien//NONSGML XoWiki Content Flow//EN" -method request
      set ical [$items as_ical]
      lappend tokens [mime::initialize  -canonical text/calendar  -param [list method request]  -param [list charset UTF-8]  -header [list "Content-Disposition" "attachment; filename=\"todo.vcs\""]  -encoding "quoted-printable" -string $ical]
      lappend tokens [mime::initialize  -canonical application/ics -param [list name "invite.ics"]  -header [list "Content-Disposition" "attachment; filename=\"todo.ics\""]  -encoding "quoted-printable" -string $ical]
    }

    if {[llength $tokens]>1} {
      set tokens [mime::initialize -canonical "multipart/mixed" -parts $tokens]
    }

    set headers_list [list]
    lappend headers_list  [list From $from_info(email)]  [list To $to_info(email)]  [list Subject $subject]

    set originator [acs_mail_lite::bounce_address -user_id $from  -package_id ${:package_id}  -message_id $message_id]

    acs_mail_lite::smtp -multi_token $tokens -headers $headers_list -originator $originator
    mime::finalize $tokens -subordinates all

  }
::xowf::WorkflowPage instproc get_form_constraints {{-trylocal false}} {
    #:log ""
    if {[:istype ::xowiki::FormPage] && [:is_wf]} {
      #:msg "get_form_constraints is_wf"
      return [::xo::cc cache [list [self] wf_merged_form_constraints [next]]]
    } elseif {[:istype ::xowiki::FormPage] && [:is_wf_instance]} {
      #:msg "get_form_constraints is_wf_instance"
      return [::xo::cc cache [list [self] wfi_merged_form_constraints [next]]]
    } else {
      #:msg "get_form_constraints next"
      next
    }
  }
::xowf::WorkflowPage instproc get_assignee assigned_to {
    return [:assignee]
  }
::xowf::WorkflowPage instproc www-create-or-use {{-parent_id:integer 0} {-view_method:wordchar edit} {-name ""} {-nls_language ""}} {
    #:msg "instance = [:is_wf_instance], wf=[:is_wf]"
    if {[:is_wf]} {
      #
      # In a first step, we call "allocate". Allocate is an Action
      # defined in a workflow, which is called *before* the workflow
      # instance is created. Via allocate, it is e.g. possible to
      # provide a computed name for the workflow instance from within
      # the workflow definition.
      #
      set ctx [::xowf::Context require [self]]
      set wfc [$ctx wf_container]
      :activate $ctx allocate

      #
      # After allocate, the payload might contain "name", "parent_id"
      # or "m". Using the payload dict has the advantage that it does
      # not touch the instance variables.
      #
      set payload [${wfc}::allocate payload]
      #ns_log notice "AFTER ALLOCATE www-create-or-use <$payload>"
      set m ""
      set title ""
      foreach p {name title parent_id m} {
        if {[dict exists $payload $p]} {
          set $p [dict get $payload $p]
        }
      }
      set package ::${:package_id}
      if {$title ne ""} {
        ::xo::cc set_query_parameter title $title
      }

      #
      # If these values are not set, try to obtain it the old-fashioned way.
      #
      if {$parent_id == 0} {
        set parent_id [:query_parameter parent_id:cr_item_of_package,arg=${:package_id} [$package folder_id]]
      }
      if {$name eq ""} {
        set name [:property name ""]
      }

      #
      # Check, if allocate has provided a name:
      #
      if {$name ne ""} {
        # Ok, a name was provided. Check if an instance with this name
        # exists in the current folder.
        set default_lang [:lang]
        $package get_lang_and_name -default_lang $default_lang -name $name lang stripped_name
        set id [::xo::db::CrClass lookup -name $lang:$stripped_name -parent_id $parent_id]
        #:log "after allocate lookup of $lang:$stripped_name returned $id, default-lang(${:name})=$default_lang [:nls_language]"
        if {$id != 0} {
          #
          # The instance exists already. Either call method "m"
          # directly (if provided) or redirect to the item.
          #
          if {$m eq ""} {
            return [$package returnredirect  [export_vars -no_base_encode  -base [$package pretty_link -parent_id $parent_id $lang:$stripped_name]  {return_url template_file}]]
          } else {
            set item [::xo::db::CrClass get_instance_from_db -item_id $id]
            # missing: policy check.
            return [$item www-$m]
          }
        } else {
          if {$lang ne $default_lang} {
            set nls_language [:get_nls_language_from_lang $lang]
          } else {
            set nls_language [:nls_language]
          }
          #:msg "We want to create $lang:$stripped_name"
          set name $lang:$stripped_name
        }
      }
    }
    # method "m" is ignored, always edit
    next -parent_id $parent_id -view_method $view_method -name $name -nls_language $nls_language
  }
::xowf::WorkflowPage instproc merge_constraints {c1 args} {
    # Load into the base_constraints c1 the constraints from the argument list.
    # The first constraints have the lowest priority
    set fcrepo [:constraints_as_dict [:get_fc_repository]]
    set merged [:constraints_as_dict -fc_repository $fcrepo $c1]
    foreach c2 $args {
      foreach {att value} [:constraints_as_dict -fc_repository $fcrepo $c2] {
        if {[dict exists $merged $att]} {
          dict append merged $att ",$value"
        } else {
          dict set merged $att "$value"
        }
      }
    }
    return [lmap {att value} $merged {string cat $att:$value}]
  }
::xowf::WorkflowPage instproc get_action_obj -action:required {
    set ctx [::xowf::Context require [self]]
    #
    # First try to call the action in the current state
    #
    foreach a [$ctx get_actions] {
      if {[namespace tail $a] eq "$action"} {
        # In the current state, the specified action is allowed
        :log  "--xowf action $action allowed -- name='${:name}'"
        return $a
      }
    }
    #
    # Some actions are state-safe, these can be called in every state
    #
    set actionObj [$ctx wf_definition_object $action]
    if {[nsf::is object $actionObj] && [$actionObj state_safe]} {
      # The action is defined as state-safe, so if can be called in every state
      :log  "--xowf action $action state_safe -- name='${:name}'"
      return $actionObj
    }
    error "No state-safe action '$action' available in workflow instance [self] of  [${:page_template} name] in state [$ctx get_current_state]\n Available actions: [[$ctx current_state] get_actions]"
  }
::xowf::WorkflowPage instproc wf_context {{ctx {}}} {
    if {$ctx ne ""} {
      set :_wf_context $ctx
    }
    return ${:_wf_context}
  }
::xowf::WorkflowPage instproc activate {{-verbose true} ctx action} {
    #
    # Execute action and compute next state of the action.
    #
    set actionObj [$ctx wf_definition_object $action]
    #
    # Check, if action is defined.
    #
    if {![nsf::is object $actionObj]} {
      #
      # There is no such action the current context.
      #
      if {$verbose} {ns_log notice "Warning: ${:name} No action $action in workflow context"}
      return ""
    }
    #
    # Activate action
    #
    ad_try {
      $actionObj activate [self]

    } on error {errorMsg errorDict} {
      #
      # Something went wrong in the application specific
      # code. Depending on batch_mode, report the error to the user or
      # to the variable __evaluation_error in the package object.
      #
      #:log "--WF: error in action $action ERRORDICT <$errorDict>"

      set errorInfo [dict get $errorDict -errorinfo]
      set error "error in action '$action' of workflow instance ${:name} of workflow [${:page_template} name]:"
      if {[::${:package_id} exists __batch_mode]} {
        ::${:package_id} set __evaluation_error "$error\n\n$errorInfo"
        incr validation_errors
      } else {
        :msg -html 1 "$error <pre>[ns_quotehtml $errorInfo]</pre>"
      }
      ad_log error "--WF: evaluation $error\n$errorInfo"
      set next_state ""

    } on ok {result} {
      #
      # The action went ok. The call to "get_next_state" is here to
      # allow the developer to influence the outcome of
      # "get_next_state" by the activated method.
      #
      set next_state [$actionObj get_next_state]
      #:log "ACTIVATE ${:name} no error next-state <$next_state>"
    }
    return $next_state
  }
::xowf::WorkflowPage instproc evaluate_form_field_condition cond {
    set ctx [::xowf::Context require [self]]
    if {[nsf::is object ${ctx}::$cond]} {
      return [${ctx}::$cond]
    }
    return 0
  }
::xowf::WorkflowPage instproc visited_states {} {
    set item_id ${:item_id}
    foreach state [xo::dc list history {
      select DISTINCT state from xowiki_form_page p, cr_items i, cr_revisions r
      where i.item_id = :item_id and r.item_id = i.item_id and xowiki_form_page_id = r.revision_id}] {
      set visited($state) 1
    }
    #:msg "visited states of item $item_id = [array names visited]"
    return [array names visited]
  }
::xowf::WorkflowPage instproc debug_msg msg {
    #util_user_message -message $msg
    ns_log notice "--WF $msg"
    catch {ds_comment $msg}
  }
::xowf::WorkflowPage instproc constraints_as_dict {{-fc_repository ""} c} {
    set result ""
    foreach name_and_spec $c {
      set p [string first : $name_and_spec]
      if {$p > -1} {
        set spec_name [string range $name_and_spec 0 $p-1]
        set short_spec [string range $name_and_spec $p+1 end]
        if {$short_spec eq "" && [dict exists $fc_repository $spec_name]} {
          set short_spec [dict get $fc_repository $spec_name]
          #:log "======= use fc_repository for <$spec_name> <$short_spec>"
        }
        dict set result $spec_name $short_spec
      } else {
        ns_log warning "ignore invalid fc: <$name_and_spec>"
      }
    }
    return $result
  }
::xowf::WorkflowPage instproc post_process_dom_tree {dom_doc dom_root form_fields} {
    # In feedback mode, we set the CSS class to correct or incorrect

    if {[info exists :__feedback_mode]} {
      unset :__feedback_mode
      ::xo::Page requireCSS /resources/xowf/feedback.css
      set form [lindex [$dom_root selectNodes "//form"] 0]
      $form setAttribute class "[$form getAttribute class] feedback"

      #
      # In cases, where the HTML exercise was given, we process the HTML
      # to flag the result.
      #
      # TODO: What should we do with the feedback. "util_user_message" is
      # not optimal...
      #
      foreach f $form_fields {
        if {[$f exists __rendered]} continue
        if {[$f exists evaluated_answer_result]} {
          set result [$f set evaluated_answer_result]
          foreach n [$dom_root selectNodes "//form//*\[@name='[$f name]'\]"] {
            set oldCSSClass [expr {[$n hasAttribute class] ? [$n getAttribute class] : ""}]
            $n setAttribute class [string trim "$oldCSSClass [$f form_widget_CSSclass]"]
            $f form_widget_CSSclass $result

            set helpText [$f help_text]
            if {$helpText ne ""} {
              #set divNode [$dom_doc createElement div]
              #$divNode setAttribute class [$f form_widget_CSSclass]
              #$divNode appendChild [$dom_doc createTextNode $helpText]
              #[$n parentNode] insertBefore $divNode [$n nextSibling]

              #set spanNode [$dom_doc createElement span]
              #$spanNode setAttribute class "glyphicon glyphicon-ok [$f form_widget_CSSclass]"
              #[$n parentNode] insertBefore $spanNode [$n nextSibling]

              set parentNode [$n parentNode]
              set oldClass [$parentNode getAttribute class ""]
              $parentNode setAttribute class "selection [$f form_widget_CSSclass]"
              $parentNode setAttribute title $helpText

              #util_user_message -message "field [$f name], value [$f value]: $helpText"
            }
          }
        }
      }
      #
      # Provide feedback for the whole exercise.
      #
      if {[:answer_is_correct]} {
        set feedback [:get_from_template feedback_correct]
      } else {
        set feedback [:get_from_template feedback_incorrect]
      }
      if {$feedback ne ""} {
        $dom_root appendFromScript {
          html::div -class feedback {
            html::t -disableOutputEscaping $feedback
          }
        }
      }
    }
  }
::xowf::WorkflowPage instproc answer_is_correct {} {
    set correct 0
    :log "WorkflowPage(${:name}).answer_is_correct autocorrect '[:get_from_template auto_correct]' -- [string is true -strict [:get_from_template auto_correct]]"
    if {[string is true -strict [:get_from_template auto_correct]]} {
      :log "==== answer_is_correct '[:instantiated_form_fields]'"
      foreach f [:instantiated_form_fields] {
        #:log [$f serialize]
        #:log "checking correctness [$f name] [$f info class] answer?[$f exists value] correct_when ?[$f exists correct_when]"
        if {[$f exists value]} {
          set r [$f answer_is_correct]
          #:log [$f serialize]
          if {$r != 1} {
            #:log [$f serialize]
            #:log "checking correctness [$f name] failed ([$f answer_is_correct])"
            set correct -1
            break
          }
          set correct 1
        }
      }
    }
    return $correct
  }
::xowf::WorkflowPage instproc www-view {{content {}}} {
    # The edit method calls view with an HTML content as argument.
    # To avoid a loop, when "view" is redirected to "edit",
    # we make sure that we only check the redirect on views
    # without content.

    #:msg "view [self args] [:is_wf_instance]"

    if {[:is_wf_instance] && $content eq ""} {
      set ctx [::xowf::Context require [self]]
      set method [$ctx get_view_method]
      set s [$ctx current_state]
      :include_header_info -css [$s extra_css] -js [$s extra_js]

      if {$method ne "" && $method ne "view"} {
        #:msg "view redirects to $method in state [$ctx get_current_state]"
        switch -- $method {
          view_user_input {
            #:msg "calling edit with disable_input_fields=1"
            return [:www-edit -disable_input_fields 1]
          }
          view_user_input_with_feedback {
            set :__feedback_mode 1
            #:msg "calling edit with disable_input_fields=1"
            return [:www-edit -disable_input_fields 1]
          }
          default {
            #:msg "calling $method"
            return [::${:package_id} invoke -method $method]
          }
        }
      }
    }
    next
  }
::xowf::WorkflowPage instproc stats_record_count name {
    dict incr :__stats_count $name
  }
::xowf::WorkflowPage instproc check_role role {
    if {[::xo::cc info methods role=$role] eq ""} {
      :msg "ignoring unknown role '$role'"
      return 0
    }
    if {$role eq "creator"} {
      #
      # Meaning: "creator of the object", requires the object as
      # additional attribute.
      #
      return [::xo::cc role=$role  -object [self]  -user_id [::xo::cc user_id]  -package_id [:package_id]]
    } else {
      return [::xo::cc role=$role  -user_id [::xo::cc user_id]  -package_id ${:package_id}]
    }
  }
::xowf::WorkflowPage instproc get_fc_repository {} {
    set container [[:wf_context] wf_container]
    if {[$container exists fc_repository]} {
      return [$container set fc_repository]
    }
    #ns_log warning "get_fc_repository returns empty"
    return ""
  }
::xowf::WorkflowPage instproc is_wf {} {
    if {[info exists :__wf(workflow_definition)]} {
      return 1
    } elseif {[:property workflow_definition] ne ""} {
      array set :__wf ${:instance_attributes}
      return 1
    } else {
      return 0
    }
  }
::xowf::WorkflowPage instproc solution_set {} {
    set solutions [list]
    foreach f [:instantiated_form_fields] {
      if {![$f exists answer]} continue
      lappend solutions [$f name]=[$f answer]
    }
    return [join [lsort $solutions", "]
  }
::xowf::WorkflowPage instproc call_action_foreach {-action:required {-attributes ""} page_names} {
    foreach page_name $page_names {
      set page [${:package_id} get_page_from_name -parent_id [:parent_id] -name $page_name]
      if {$page ne ""} {
        $page call_action -action $action -attributes $attributes
      } else {
        ns_log notice "WF: could not call action $action, since $page_name in [:parent_id] failed"
      }
    }
  }
::xowf::WorkflowPage instproc instantiated_form_fields {} {
    # Helper method to
    #  - obtain the field_names from the current form, to
    #  - create form_field instances from that and to
    #  - provide the values from the instance attributes into it.
    lassign [:field_names_from_form] _ field_names
    set form_fields [:create_form_fields $field_names]
    :load_values_into_form_fields $form_fields
    return $form_fields
  }
::xowf::WorkflowPage instproc save_in_hstore {} {
    #
    if {[::xo::dc has_hstore] && [${:package_id} get_parameter use_hstore 0]} {
      set hkey [::xowiki::hstore::dict_as_hkey [:hstore_attributes]]
      set revision_id ${:revision_id}
      xo::dc dml update_hstore "update xowiki_page_instance  set hkey = :hkey  where page_instance_id = :revision_id"
    }
  }
::xowf::WorkflowPage instproc schedule_action {-time:required -party_id -action:required {-attributes {}}} {
    if {![:is_wf_instance]} {
      error "Page [self] is not a Workflow Instance"
    }
    if {![info exists party_id]} {
      set party_id [::xo::cc user_id]
    }
    :schedule_job -time $time -party_id $party_id  [list call_action -action $action -attributes $attributes]
  }
::xowf::WorkflowPage instproc render_thumbnails upload_info {
    dict with upload_info {
      set parent_id ${:item_id}
      set feedbackFiles [xo::dc list_of_lists . {
        select item_id, name from cr_items where parent_id = :parent_id
      }]
      if {[regexp {^([^/]+)/} $file_name . qn]} {
        set HTML [:QM render_feedback_files  -question_name $qn  -feedbackFiles $feedbackFiles]
      } else {
        set HTML "$file_name created"
      }
    }
    return $HTML
  }
::xowf::WorkflowPage instproc schedule_job {-time:required -party_id cmd} {
    :log "-at $time"
    set j [::xowf::atjob new  -time $time  -party_id $party_id  -cmd $cmd  -url [:pretty_link]  -object [self]]
    $j persist
  }
::xowf::WorkflowPage instproc default_instance_attributes {} {
    # Provide the default list of instance attributes to derived
    # FormPages.
    if {[:is_wf]} {
      #
      # We have a workflow page. Get the initial state of the workflow
      # instance from the workflow.
      #
      set instance_attributes ""
      set ctx [::xowf::Context require [self]]
      foreach p [$ctx defined ::xowiki::formfield::FormField] {
        set name [$p name]
        set value [$p default]
        if {[::xo::cc exists_query_parameter $name]} {
          #
          # Never clobber instance attributes from query parameters
          # blindly.
          #
          #:msg "ignore $name"
          continue
        }
        if {[::xo::cc exists_query_parameter p.$name]
            && [$p exists allow_query_parameter]} {
          #
          # We allow the value to be taken from the query parameter.
          #
          set value [::xo::cc query_parameter p.$name]
          $p value $value
          $p validate $p
        }
        dict set instance_attributes $name $value
        set f($name$p
      }

      ## save instance attributes
      #set instance_attributes [array get __ia]
      #:msg "[self] ${:name} setting default parameter"
      #:log ia=$instance_attributes,props=[$ctx defined Property]

      :state [$ctx get_current_state]
      #:msg "setting initial state to '[:state]'"

      return $instance_attributes
    } else {
      next
    }
  }
::xowf::WorkflowPage instproc footer {} {
    if {[info exists :__no_form_page_footer]} {
      next
    } else {
      set parent_id [:parent_id]
      set form_item_id ${:page_template}
      #:msg "is wf page [:is_wf], is wf instance page [:is_wf_instance]"
      if {[:is_wf]} {
        #
        # page containing a work flow definition
        #
        #set ctx [::xowf::Context require [self]]
        set work_flow_form [::xo::db::CrClass get_instance_from_db -item_id $form_item_id]
        set work_flow_base [$work_flow_form pretty_link]

        set wf [self]
        set wf_base [$wf pretty_link]
        set button_objs [list]

        # create new workflow instance button with start form
        #if {[:parent_id] != [::${:package_id} folder_id]} {
        #  set parent_id [:parent_id]
        #}
        set link [::${:package_id} make_link -link $wf_base $wf create-new parent_id return_url]
        lappend button_objs [::xowiki::includelet::form-menu-button-new new -volatile  -parent_id $parent_id  -form $wf -link $link]

        # list workflow instances button
        set obj [::xowiki::includelet::form-menu-button-wf-instances new -volatile  -package_id ${:package_id} -parent_id $parent_id  -base $wf_base -form $wf]
        if {[info exists return_url]} {
          $obj return_url $return_url
        }
        lappend button_objs $obj

        # work flow definition button
        set obj [::xowiki::includelet::form-menu-button-form new -volatile  -package_id ${:package_id} -parent_id $parent_id  -base $work_flow_base -form $work_flow_form]
        if {[info exists return_url]} {$obj return_url $return_url}
        lappend button_objs $obj

        # make menu
        return [:include [list form-menu -form_item_id ${:item_id} -button_objs $button_objs]]

      } elseif {[:is_wf_instance]} {
        #
        # work flow instance
        #
        set entry_form_item_id [:wf_property wf_form_id]
        set work_flow_form [::xo::db::CrClass get_instance_from_db -item_id $form_item_id]
        set work_flow_base [$work_flow_form pretty_link]
        set button_objs [list]

        #:msg entry_form_item_id=$entry_form_item_id-exists?=[nsf::is object $entry_form_item_id]

        # form definition button
        if {![nsf::is object $entry_form_item_id]} {
          # In case, the id is a form object, it is a dynamic form,
          # that we can't edit; therefore, we provide no link.
          #
          # Here, we have an id that we use for fetching...
          #
          set form [::xo::db::CrClass get_instance_from_db -item_id $entry_form_item_id]
          set base [$form pretty_link]
          set obj [::xowiki::includelet::form-menu-button-form new -volatile  -package_id ${:package_id} -parent_id $parent_id  -base $base -form $form]
          if {[info exists return_url]} {
            $obj return_url $return_url
          }
          lappend button_objs $obj
        }
        #
        # work flow definition button
        #
        set obj [::xowiki::includelet::form-menu-button-wf new -volatile  -package_id ${:package_id} -parent_id $parent_id  -base $work_flow_base -form $work_flow_form]
        if {[info exists return_url]} {$obj return_url $return_url}
        lappend button_objs $obj
        # make menu
        return [:include [list form-menu -form_item_id ${:page_template} -button_objs $button_objs]]
      } else {
        next
      }
    }
  }
::xowf::WorkflowPage instproc util_user_message {-html:switch -message} {
    if {[ns_conn isconnected]} {
      ::util_user_message -message $message -html=$html
    } else {
      ns_log notice "util_user_message suppressed (no connection): $message"
    }
  }
::xowf::WorkflowPage instproc save_new args {
    set r [next]
    :save_in_hstore
    return $r
  }
::xowf::WorkflowPage instproc hstore_attributes {} {
    #
    # We do not want to save the workflow definition in every workflow
    # instance.
    #
    return [dict remove ${:instance_attributes} workflow_definition]
  }
::xowf::WorkflowPage instproc save args {
    set r [next]
    :save_in_hstore
    return $r
  }
::xowf::WorkflowPage instproc save_data args {
    if {[:is_wf_instance]} {
      #
      # update the state in the workflow instance
      #
      set ctx [::xowf::Context require [self]]
      set prev_state ${:state}
      set :state [$ctx get_current_state]

      if {$prev_state ne ${:state}} {
        # The form object in the cache is still that from the previous
        # state, make sure we flush it.
        $ctx flush_form_object
      }
    }
    next
  }
::xowf::WorkflowPage instproc childpage {-name:required -form} {
    if {[info exists form]} {
      set child_page_id [::${:package_id} lookup  -use_package_path false  -default_lang en  -name $name  -parent_id ${:item_id}]
      if {$child_page_id == 0} {
        ns_log notice "child page '$name' does not exist"
        set form_obj [::${:package_id} instantiate_forms  -default_lang "en"  -forms $form]
        if {[llength $form_obj] == 0} {
          error "childpage: cannot instantiate $form"
        }
        set p [$form_obj create_form_page_instance  -name $name  -nls_language en_US  -parent_id ${:item_id}  -package_id ${:package_id}  -instance_attributes {}]
        $p save_new
      } else {
        #ns_log notice "child page '$name' exists already (item_id $child_page_id)"
        set p [::xo::db::CrClass get_instance_from_db -item_id $child_page_id]
      }
      return $p
    } else {
      error "cannot create '$name': API supports so far only form pages"
    }
 }
::xowf::WorkflowPage instproc get_template_object {} {
    if {[:is_wf_instance]} {
      set key :__wfi(wf_form_id)
      if {![info exists $key]} {
        set ctx [::xowf::Context require [self]]
        set $key [$ctx form_object [self]]
      }
      set form_obj [set $key]
      if {![nsf::is object $form_obj]} {
        ad_log error "deprecated usage: method 'form_object' did NOT return an object. Will raise an error in the future"
        set form_id [string trimleft $form_obj :]
        set form_obj [::xo::db::CrClass get_instance_from_db -item_id $form_id]
      }
      return $form_obj
    } else {
      return [next]
    }
  }
::xowf::WorkflowPage instproc wf_merged_form_constraints constraints_from_form {
    return $constraints_from_form
    #return [:merge_constraints $constraints_from_form [:property form_constraints]]
  }
::xowf::WorkflowPage instproc initialize {} {
    #:log "START-initialize is_wf_instance [:is_wf_instance]"
    #
    # A fresh workflow page was created (called only once per
    # workflow page at initial creation)
    #
    if {[:is_wf_instance]} {
      #
      # Get context and call user defined "constructor"
      #
      # set the state to a well defined starting point
      if {${:state} eq ""} {set :state initial}

      set ctx [::xowf::Context require -new [self]]
      :activate -verbose false $ctx initialize

      # Ignore the returned next_state, since the initial state is
      # always set to the same value from the ctx (initial)
      #:msg "[self] is=${:instance_attributes}"

    } elseif {[:is_wf] && [info exists :item_id]} {
      #
      # We are initializing a fully created workflow object.
      #
      # The test for "exists :item_id" is important, since when a
      # workflow is created via "create_form_page_instance", the
      # workflow object is create via "new", it has not been saved yet
      # and has therefore no "item_id" yet.
      #

      set ctx [::xowf::Context require -new [self]]
      set code [[$ctx wf_container] wf-specific]
      #ns_log notice "...initialize wf, wf-specific code: $code"
      if {$code ne ""} {
        eval $code
      }
    }
    next
    #:log END-initialize
  }
::xowf::WorkflowPage instproc get_form_data args {
    if {[:is_wf_instance]} {
      lassign [next] validation_errors category_ids
      if {$validation_errors == 0} {
        #:msg "validation ok"
        set ctx [::xowf::Context require [self]]
        set cc [${:package_id} context]
        foreach {name value} [$cc get_all_form_parameter] {
          if {[regexp {^__action_(.+)$} $name _ action]} {
            set actionObj [:get_action_obj -action $action]
            set next_state [:activate $ctx $action]
            #:log "after activate next_state=$next_state, current_state=[$ctx get_current_state], ${:instance_attributes}"
            if {$next_state ne ""} {
              if {[$actionObj exists assigned_to]} {
                :assignee [:get_assignee [$actionObj assigned_to]]
              }
              $ctx set_current_state $next_state
            }
            break
          }
        }
      }
      #ns_log notice "===== get_form_data returns [list $validation_errors $category_ids]"
      return [list $validation_errors $category_ids]
    } else {
      next
    }
  }
::xowf::WorkflowPage instproc is_wf_instance {} {
    if {[array exists :__wfi]} {
      return 1
    }
    #
    # We cannot call get_template_object here, because this will lead
    # to a recursive loop.
    #
    if {![nsf::is object ::${:page_template}]} {
      ::xo::db::CrClass get_instance_from_db -item_id ${:page_template}
    }
    if {${:state} ne ""
        && [${:page_template} hasclass ::xowf::WorkflowPage]
        && [${:page_template} is_wf]
      } {
      array set :__wfi [${:page_template} instance_attributes]
      return 1
    }
    return 0
  }
::xowf::WorkflowPage instproc render_form_action_buttons_widgets {{-CSSclass ""} buttons} {
    if {[llength $buttons] > 0} {
      #
      # Build button groups based on "form_button_wrapper_CSSclass".
      #
      set previous_wrapper_class "NONE"
      set wrapper_groups {}
      set group_num 0
      foreach f $buttons {
        set wrapper_class [$f form_button_wrapper_CSSclass]
        if {$wrapper_class eq $previous_wrapper_class} {
          dict lappend wrapper_groups [list $wrapper_class $group_num$f
          continue
        }
        incr group_num
        dict lappend wrapper_groups [list $wrapper_class $group_num$f
        set previous_wrapper_class $wrapper_class
      }

      foreach wrapper_group [dict keys $wrapper_groups] {
        ::html::div -class [lindex $wrapper_group 0] {
          foreach f [dict get $wrapper_groups $wrapper_group] {
            $f render_input
          }
        }
      }
    }
  }
::xowf::WorkflowPage instproc stats_record_detail {-label -value -name -correctly_answered:boolean} {
    dict set :__stats_label $name label $value $label
    if {[info exists :__stats_success] && [dict exists ${:__stats_success} $name $value]} {
      set details [dict get ${:__stats_success} $name $value]
    } else {
      set details ""
    }
    dict incr details $correctly_answered
    dict set :__stats_success $name $value $details
  }
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: