Index: openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql,v diff -u -N -r1.18 -r1.19 --- openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql 10 Dec 2003 16:05:09 -0000 1.18 +++ openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql 16 Dec 2003 18:18:49 -0000 1.19 @@ -98,6 +98,8 @@ constraint wf_roles_pk primary key, workflow_id integer + constraint wf_roles_workflow_id_nn + not null constraint wf_roles_workflow_id_fk references workflows(workflow_id) on delete cascade, @@ -245,6 +247,8 @@ -- For the initial action, which fires when a new case is started create table workflow_initial_action ( workflow_id integer + constraint wf_roles_workflow_id_nn + not null constraint wf_initial_acn_pk primary key constraint wf_initial_acn_wf_fk Index: openacs-4/packages/workflow/sql/postgresql/upgrade/upgrade-1.2-2.0d1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/postgresql/upgrade/upgrade-1.2-2.0d1.sql,v diff -u -N -r1.2 -r1.3 --- openacs-4/packages/workflow/sql/postgresql/upgrade/upgrade-1.2-2.0d1.sql 1 Dec 2003 09:53:19 -0000 1.2 +++ openacs-4/packages/workflow/sql/postgresql/upgrade/upgrade-1.2-2.0d1.sql 16 Dec 2003 18:18:49 -0000 1.3 @@ -51,3 +51,8 @@ alter table workflow_fsm_states add constraint wf_fsm_states_short_name_un unique (workflow_id, short_name); alter table workflow_fsm_states add constraint wf_fsm_states_pretty_name_un unique (workflow_id, pretty_name); + + +-- New not null constraints +alter table workflow_initial_action alter column workflow_id set not null; +alter table workflow_roles alter column workflow_id set not null; Index: openacs-4/packages/workflow/tcl/action-procs-oracle.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/action-procs-oracle.xql,v diff -u -N -r1.4 -r1.5 --- openacs-4/packages/workflow/tcl/action-procs-oracle.xql 1 Dec 2003 09:53:19 -0000 1.4 +++ openacs-4/packages/workflow/tcl/action-procs-oracle.xql 16 Dec 2003 18:18:49 -0000 1.5 @@ -12,12 +12,18 @@ - + - timeout_seconds = :attr_timeout_seconds + timeout_seconds + + + :attr_timeout_seconds + + + select a.action_id, Index: openacs-4/packages/workflow/tcl/action-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/action-procs-postgresql.xql,v diff -u -N -r1.4 -r1.5 --- openacs-4/packages/workflow/tcl/action-procs-postgresql.xql 1 Dec 2003 09:53:19 -0000 1.4 +++ openacs-4/packages/workflow/tcl/action-procs-postgresql.xql 16 Dec 2003 18:18:49 -0000 1.5 @@ -13,12 +13,18 @@ - + - timeout = [ad_decode $attr_timeout_seconds "" "null" "interval '$attr_timeout_seconds seconds'"] + timeout + + + [ad_decode $attr_timeout_seconds "" "null" "interval '$attr_timeout_seconds seconds'"] + + + select a.action_id, Index: openacs-4/packages/workflow/tcl/action-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/action-procs.tcl,v diff -u -N -r1.16 -r1.17 --- openacs-4/packages/workflow/tcl/action-procs.tcl 11 Dec 2003 15:29:03 -0000 1.16 +++ openacs-4/packages/workflow/tcl/action-procs.tcl 16 Dec 2003 18:18:49 -0000 1.17 @@ -91,238 +91,302 @@ @return The id of the created action - @see workflow::action::fsm::new + @see workflow::action::edit + @see workflow::action::fsm::edit @see workflow::definition_changed_handler @author Peter Marklund } { - db_transaction { - # Insert basic action info - if { [empty_string_p $sort_order] } { - set sort_order [workflow::default_sort_order \ - -workflow_id $workflow_id \ - -table_name "workflow_actions"] - } else { - workflow::action::update_sort_order \ - -workflow_id $workflow_id \ - -sort_order $sort_order - } - - set short_name [workflow::action::generate_short_name \ - -workflow_id $workflow_id \ - -pretty_name $pretty_name \ - -short_name $short_name] - - if { [empty_string_p $action_id] } { - set action_id [db_nextval "workflow_actions_seq"] - } - - if { [empty_string_p $assigned_role] } { - set assigned_role_id [db_null] - } else { - set assigned_role_id [workflow::role::get_id \ - -workflow_id $workflow_id \ - -short_name $assigned_role] - if { [empty_string_p $assigned_role_id] } { - error "Cannot find role '$assigned_role' to be the assigned role for action '$short_name'" - } - } - - # Insert the action - db_dml insert_action {} - - # Set all the other attributes - array set update_cols [list] - set update_cols(allowed_roles) $allowed_roles - set update_cols(privileges) $privileges - set update_cols(callbacks) $callbacks - set update_cols(initial_action_p) $initial_action_p - - workflow::action::edit \ - -internal \ - -action_id $action_id \ - -workflow_id $workflow_id \ - -array update_cols - - if { !$internal_p } { - workflow::definition_changed_handler -workflow_id $workflow_id - } + # Wrapper for workflow::action::edit + + array set row [list] + foreach col { + initial_action_p sort_order short_name pretty_name + pretty_past_tense edit_fields allowed_roles assigned_role + privileges callbacks always_enabled_p description description_mime_type + timeout_seconds + } { + set row($col) [set $col] } - if { !$internal_p } { - # Flush the workflow cache, as changing an action changes the entire workflow - # e.g. initial_action_p, enabled_in_states. - workflow::flush_cache -workflow_id $workflow_id - } - + set action_id [workflow::action::edit \ + -operation "insert" \ + -action_id $action_id \ + -workflow_id $workflow_id \ + -array row] + return $action_id } ad_proc -public workflow::action::edit { - {-action_id:required} + {-operation "update"} + {-action_id {}} {-workflow_id {}} - {-array:required} + {-array {}} {-internal:boolean} } { Edit an action. - @param action_id The action to edit. + Attributes of the array: - @param workflow_id Optionally specify the workflow_id. If not specified, we will execute a query to find it. + short_name + pretty_name + pretty_past_tense + edit_fields + description + description_mime_type + sort_order + always_enabled_p + assigned_role + timeout_seconds + privileges + allowed_roles + initial_action_p + callbacks - @param array Name of an array in the caller's namespace with attributes to edit. + @param operation insert, update, delete + @param action_id For update/delete: The action to update or delete. + For insert: Optionally specify a pre-generated action_id for the action. + + @param workflow_id For update/delete: Optionally specify the workflow_id. If not specified, we will execute a query to find it. + For insert: The workflow_id of the new action. + + @param array For insert/update: Name of an array in the caller's namespace with attributes to insert/update. + @param internal Set this flag if you're calling this proc from within the corresponding proc for a particular workflow model. Will cause this proc to not flush the cache or call workflow::definition_changed_handler, which the caller must then do. @return action_id + @author Lars Pind (lars@collaboraid.biz) + @see workflow::action::new } { - upvar 1 $array row - if { ![array exists row] } { - error "Array $array does not exist or is not an array" + switch $operation { + update - delete { + if { [empty_string_p $action_id] } { + error "You must specify the action_id of the action to $operation." + } + } + insert {} + default { + error "Illegal operation '$operation'" + } } - foreach name [array names row] { - set missing_elm($name) 1 + switch $operation { + insert - update { + upvar 1 $array row + if { ![array exists row] } { + error "Array $array does not exist or is not an array" + } + foreach name [array names row] { + set missing_elm($name) 1 + } + } } - - if { [empty_string_p $workflow_id] } { - set workflow_id [workflow::action::get_element \ - -action_id $action_id \ - -element workflow_id] + switch $operation { + insert { + if { [empty_string_p $workflow_id] } { + error "You must supply workflow_id" + } + # Default sort_order + if { ![exists_and_not_null row(sort_order)] } { + set row(sort_order) [workflow::default_sort_order \ + -workflow_id $workflow_id \ + -table_name "workflow_actions"] + } + # Default short_name on insert + if { ![info exists row(short_name)] } { + set row(short_name) {} + } + } + update - delete { + if { [empty_string_p $workflow_id] } { + set workflow_id [workflow::action::get_element \ + -action_id $action_id \ + -element workflow_id] + } + } } - set set_clauses [list] - - # Handle columns in the workflow_actions table - foreach attr { - short_name pretty_name pretty_past_tense edit_fields description description_mime_type sort_order - always_enabled_p - assigned_role - timeout_seconds - } { - if { [info exists row($attr)] } { - set varname attr_$attr - # Convert the Tcl value to something we can use in the query - switch $attr { - short_name { - if { ![exists_and_not_null row(pretty_name)] } { - if { [empty_string_p $row(short_name)] } { - error "You cannot edit with an empty short_name without also setting pretty_name" - } else { - set row(pretty_name) {} + # Parse column values + switch $operation { + insert - update { + set update_clauses [list] + set insert_names [list] + set insert_values [list] + # Handle columns in the workflow_actions table + foreach attr { + short_name pretty_name pretty_past_tense edit_fields description description_mime_type sort_order + always_enabled_p + assigned_role + timeout_seconds + } { + if { [info exists row($attr)] } { + set varname attr_$attr + # Convert the Tcl value to something we can use in the query + switch $attr { + short_name { + if { ![exists_and_not_null row(pretty_name)] } { + if { [empty_string_p $row(short_name)] } { + error "You cannot edit with an empty short_name without also setting pretty_name" + } else { + set row(pretty_name) {} + } + } + + set $varname [workflow::action::generate_short_name \ + -workflow_id $workflow_id \ + -pretty_name $row(pretty_name) \ + -short_name $row(short_name) \ + -action_id $action_id] } + always_enabled_p { + set $varname [db_boolean [template::util::is_true $row($attr)]] + } + assigned_role { + if { [empty_string_p $row($attr)] } { + set $varname [db_null] + } else { + # Get role_id by short_name + set $varname [workflow::role::get_id \ + -workflow_id $workflow_id \ + -short_name $row($attr)] + } + } + default { + set $varname $row($attr) + } } - - set $varname [workflow::action::generate_short_name \ - -workflow_id $workflow_id \ - -pretty_name $row(pretty_name) \ - -short_name $row(short_name) \ - -action_id $action_id] - } - always_enabled_p { - set $varname [db_boolean [template::util::is_true $row($attr)]] - } - assigned_role { - if { [empty_string_p $row($attr)] } { - set $varname [db_null] - } else { - # Get role_id by short_name - set $varname [workflow::role::get_id \ - -workflow_id $workflow_id \ - -short_name $row($attr)] + # Add the column to the insert/update statement + switch $attr { + timeout_seconds { + lappend update_clauses "[db_map update_timeout_seconds_name] = [db_map update_timeout_seconds_value]" + lappend insert_names [db_map update_timeout_seconds_name] + lappend insert_values [db_map update_timeout_seconds_value] + } + default { + lappend update_clauses "$attr = :$varname" + lappend insert_names $attr + lappend insert_values :$varname + } } + if { [info exists missing_elm($attr)] } { + unset missing_elm($attr) + } } - default { - set $varname $row($attr) - } } - # Add the column to the SET clause - switch $attr { - timeout_seconds { - lappend set_clauses [db_map update_timeout_seconds] - } - default { - lappend set_clauses "$attr = :$varname" - } - } - unset missing_elm($attr) } } db_transaction { - if { [info exists row(sort_order)] } { - workflow::action::update_sort_order \ - -workflow_id $workflow_id \ - -sort_order $row(sort_order) - } - - # Update action - if { [llength $set_clauses] > 0 } { - db_dml update_action " - update workflow_actions - set [join $set_clauses ", "] - where action_id = :action_id - " - } - - # Record which roles are allowed to take action - if { [info exists row(allowed_roles)] } { - db_dml delete_allowed_roles { - delete from workflow_action_allowed_roles - where action_id = :action_id + # Sort_order + switch $operation { + insert - update { + if { [info exists row(sort_order)] } { + workflow::action::update_sort_order \ + -workflow_id $workflow_id \ + -sort_order $row(sort_order) + } } - foreach allowed_role $row(allowed_roles) { - db_dml insert_allowed_role {} - } - unset missing_elm(allowed_roles) } - - # Record which privileges enable the action - if { [info exists row(privileges)] } { - db_dml delete_privileges { - delete from workflow_action_privileges - where action_id = :action_id + # Do the insert/update/delete + switch $operation { + insert { + if { [empty_string_p $action_id] } { + set action_id [db_nextval "workflow_actions_seq"] + } + + lappend insert_names action_id + lappend insert_values :action_id + lappend insert_names workflow_id + lappend insert_values :workflow_id + + db_dml insert_action " + insert into workflow_actions + ([join $insert_names ", "]) + values + ([join $insert_values ", "]) + " } - foreach privilege $row(privileges) { - db_dml insert_privilege {} + update { + if { [llength $update_clauses] > 0 } { + db_dml update_action " + update workflow_actions + set [join $update_clauses ", "] + where action_id = :action_id + " + } } - unset missing_elm(privileges) - } - - # Record if this is an initial action - if { [info exists row(initial_action_p)] } { - if { [template::util::is_true $row(initial_action_p)] } { - db_dml delete_initial_action { - delete from workflow_initial_action - where workflow_id = :workflow_id + delete { + db_dml delete_action { + delete from workflow_actions + where action_id = :action_id } - db_dml insert_initial_action {} } - unset missing_elm(initial_action_p) } + + switch $operation { + insert - update { + # Record which roles are allowed to take action + if { [info exists row(allowed_roles)] } { + db_dml delete_allowed_roles { + delete from workflow_action_allowed_roles + where action_id = :action_id + } + foreach allowed_role $row(allowed_roles) { + db_dml insert_allowed_role {} + } + unset missing_elm(allowed_roles) + } + + # Record which privileges enable the action + if { [info exists row(privileges)] } { + db_dml delete_privileges { + delete from workflow_action_privileges + where action_id = :action_id + } + foreach privilege $row(privileges) { + db_dml insert_privilege {} + } + unset missing_elm(privileges) + } + + # Record if this is an initial action + if { [info exists row(initial_action_p)] } { + if { [template::util::is_true $row(initial_action_p)] } { + db_dml delete_initial_action { + delete from workflow_initial_action + where workflow_id = :workflow_id + } + db_dml insert_initial_action {} + } + unset missing_elm(initial_action_p) + } - # Callbacks - if { [info exists row(callbacks)] } { - db_dml delete_callbacks { - delete from workflow_action_callbacks - where action_id = :action_id + # Callbacks + if { [info exists row(callbacks)] } { + db_dml delete_callbacks { + delete from workflow_action_callbacks + where action_id = :action_id + } + foreach callback_name $row(callbacks) { + workflow::action::callback_insert \ + -action_id $action_id \ + -name $callback_name + } + unset missing_elm(callbacks) + } + + # Check that there are no unknown attributes + if { [llength [array names missing_elm]] > 0 } { + error "Trying to set illegal action attributes: [join [array names missing_elm] ", "]" + } } - foreach callback_name $row(callbacks) { - workflow::action::callback_insert \ - -action_id $action_id \ - -name $callback_name - } - unset missing_elm(callbacks) } - # Check that there are no unknown attributes - if { [llength [array names missing_elm]] > 0 } { - error "Trying to set illegal action attributes: [join [array names missing_elm] ", "]" - } - if { !$internal_p } { workflow::definition_changed_handler -workflow_id $workflow_id } @@ -337,6 +401,16 @@ return $action_id } +ad_proc -public workflow::action::delete { + {-action_id:required} +} { + Delete action with given id. + + @author Peter Marklund +} { + workflow::action::edit -operation "delete" -action_id $action_id +} + ad_proc -public workflow::action::get_assigned_role { {-action_id:required} } { @@ -625,200 +699,275 @@ @return the new action_id. - @see workflow::action::new + @see workflow::action::fsm::edit @author Peter Marklund } { + # Wrapper for workflow::action::edit - db_transaction { - # Generic workflow data: - set action_id [workflow::action::new \ - -internal \ - -initial_action_p $initial_action_p \ - -workflow_id $workflow_id \ - -action_id $action_id \ - -sort_order $sort_order \ - -short_name $short_name \ - -pretty_name $pretty_name \ - -pretty_past_tense $pretty_past_tense \ - -edit_fields $edit_fields \ - -allowed_roles $allowed_roles \ - -assigned_role $assigned_role \ - -privileges $privileges \ - -callbacks $callbacks \ - -always_enabled_p $always_enabled_p \ - -description $description \ - -description_mime_type $description_mime_type \ - -timeout_seconds $timeout_seconds] - - # FSM specific information below - - # Record whether the action changes state - if { ![empty_string_p $new_state] } { - if { ![empty_string_p $new_state_id] } { - error "You cannot supply both new_state (takes short_name) and new_state_id (takes state_id)" - } - set new_state_id [workflow::state::fsm::get_id \ - -workflow_id $workflow_id \ - -short_name $new_state] + array set row [list] + foreach col { + initial_action_p sort_order short_name pretty_name + pretty_past_tense edit_fields allowed_roles assigned_role + privileges callbacks always_enabled_p description description_mime_type + timeout_seconds + } { + set row($col) [set $col] + } + foreach elm { + new_state new_state_id + enabled_states assigned_states + enabled_state_ids assigned_state_ids + } { + if { [exists_and_not_null $elm] } { + set row($elm) [set $elm] } - db_dml insert_fsm_action {} + } - array set update_cols [list] - foreach col { enabled_states enabled_state_ids assigned_states assigned_state_ids } { - if { ![empty_string_p [set $col]] } { - set update_cols($col) [set $col] - } - } + set action_id [workflow::action::fsm::edit \ + -operation "insert" \ + -action_id $action_id \ + -workflow_id $workflow_id \ + -array row] - workflow::action::fsm::edit \ - -internal \ - -action_id $action_id \ - -workflow_id $workflow_id \ - -array update_cols - - workflow::definition_changed_handler -workflow_id $workflow_id - } - - # Flush the workflow cache, as changing an action changes the entire workflow - # e.g. initial_action_p, enabled_in_states. - workflow::flush_cache -workflow_id $workflow_id - return $action_id } ad_proc -public workflow::action::fsm::edit { - {-action_id:required} + {-operation "update"} + {-action_id {}} {-workflow_id {}} - {-array:required} + {-array {}} {-internal:boolean} } { - Edit an FSM action. + Edit an action. - @param action_id The action to edit. + @param operation insert, update, delete - @param workflow_id Optionally specify the workflow_id. If not specified, we will execute a query to find it. + @param action_id For update/delete: The action to update or delete. + For insert: Optionally specify a pre-generated action_id for the action. + + @param workflow_id For update/delete: Optionally specify the workflow_id. If not specified, we will execute a query to find it. + For insert: The workflow_id of the new action. - @param array Name of an array in the caller's namespace with attributes to edit. + @param array For insert/update: Name of an array in the caller's namespace with attributes to insert/update. - @param internal Set this flag if you're calling this proc from within the corresponding proc - for a particular workflow model. Will cause this proc to not flush the cache - or call workflow::definition_changed_handler, which the caller must then do. + @param internal Set this flag if you're calling this proc from within the corresponding proc + for a particular workflow model. Will cause this proc to not flush the cache + or call workflow::definition_changed_handler, which the caller must then do. @return action_id - @see workflow::action::fsm::new + @see workflow::action::edit } { - upvar 1 $array org_row - if { ![array exists org_row] } { - error "Array $array does not exist or is not an array" + switch $operation { + update - delete { + if { [empty_string_p $action_id] } { + error "You must specify the action_id of the action to $operation." + } + } + insert {} + default { + error "Illegal operation '$operation'" + } } + switch $operation { + insert - update { + upvar 1 $array org_row + if { ![array exists org_row] } { + error "Array $array does not exist or is not an array" + } + array set row [array get org_row] + } + } + switch $operation { + insert { + if { [empty_string_p $workflow_id] } { + error "You must supply workflow_id" + } + } + update - delete { + if { [empty_string_p $workflow_id] } { + set workflow_id [workflow::action::get_element \ + -action_id $action_id \ + -element workflow_id] + } + } + } - # We make a copy here and work on that, so the check for illegal attributes in workflow::action::edit works properly, - # i.e. we delete from 'row' before calling workflow::action::edit, but we don't touch the caller's row - array set row [array get org_row] + # Parse column values + switch $operation { + insert - update { + # Special-case: array entry new_state (short_name) and new_state_id (state_id) -- DB column is new_state (state_id) + if { [info exists row(new_state)] } { + if { [info exists row(new_state_id)] } { + error "You cannot supply both new_state (takes short_name) and new_state_id (takes state_id)" + } + if { ![empty_string_p $row(new_state)] } { + set row(new_state_id) [workflow::state::fsm::get_id \ + -workflow_id $workflow_id \ + -short_name $row(new_state)] + } else { + set row(new_state_id) [db_null] + } + unset row(new_state) + } - if { [empty_string_p $workflow_id] } { - set workflow_id [workflow::action::get_element \ - -action_id $action_id \ - -element workflow_id] - } + set update_clauses [list] + set insert_names [list] + set insert_values [list] - db_transaction { + # Handle columns in the workflow_fsm_actions table + foreach attr { + new_state_id + } { + if { [info exists row($attr)] } { + set varname attr_$attr + # Convert the Tcl value to something we can use in the query + switch $attr { + new_state_id { + set varname attr_new_state + set $varname $row($attr) + unset row($attr) + set attr new_state + } + default { + set $varname $row($attr) + } + } + # Add the column to the insert/update statement + switch $attr { + default { + lappend update_clauses "$attr = :$varname" + lappend insert_names $attr + lappend insert_values :$varname + } + } + if { [info exists row($attr)] } { + unset row($attr) + } + } + } - # Record whether the action changes state - if { [info exists row(new_state)] } { - if { [info exists row(new_state_id)] } { - error "You cannot supply both new_state (takes short_name) and new_state_id (takes state_id)" + if { [info exists row(enabled_states)] } { + if { [info exists row(enabled_state_ids)] } { + error "You cannot supply both enabled_states and enabled_state_ids" + } + set row(enabled_state_ids) [list] + foreach state_short_name $row(enabled_states) { + lappend row(enabled_state_ids) [workflow::state::fsm::get_id \ + -workflow_id $workflow_id \ + -short_name $state_short_name] + } + unset row(enabled_states) } - if { ![empty_string_p $row(new_state)] } { - set row(new_state_id) [workflow::state::fsm::get_id \ - -workflow_id $workflow_id \ - -short_name $new_state] - } else { - set row(new_state_id) [db_null] + if { [info exists row(assigned_states)] } { + if { [info exists row(assigned_state_ids)] } { + error "You cannot supply both assigned_states and assigned_state_ids" + } + set row(assigned_state_ids) [list] + foreach state_short_name $row(assigned_states) { + lappend row(assigned_state_ids) [workflow::state::fsm::get_id \ + -workflow_id $workflow_id \ + -short_name $state_short_name] + } + unset row(assigned_states) } - unset row(new_state) - } - if { [info exists row(new_state_id)] } { - set new_state_id $row(new_state_id) - db_dml update_fsm_action {} - unset row(new_state_id) + # Handle auxillary rows + array set aux [list] + foreach attr { + enabled_state_ids assigned_state_ids + } { + if { [info exists row($attr)] } { + set aux($attr) $row($attr) + unset row($attr) + } + } } + } + + db_transaction { + # Base row + set action_id [workflow::action::edit \ + -internal \ + -operation $operation \ + -action_id $action_id \ + -workflow_id $workflow_id \ + -array row] + # FSM action row + switch $operation { + insert { + lappend insert_names action_id + lappend insert_values :action_id - # Record in which states the action is enabled but not assigned - if { [info exists row(enabled_states)] } { - set assigned_p "f" - db_dml delete_enabled_states {} - foreach state_short_name $row(enabled_states) { - set enabled_state_id [workflow::state::fsm::get_id \ - -workflow_id $workflow_id \ - -short_name $state_short_name] - db_dml insert_enabled_state {} + db_dml insert_action " + insert into workflow_fsm_actions + ([join $insert_names ", "]) + values + ([join $insert_values ", "]) + " } - unset row(enabled_states) - } elseif { [info exists row(enabled_state_ids)] } { - set assigned_p "f" - db_dml delete_enabled_states {} - foreach enabled_state_id $row(enabled_state_ids) { - db_dml insert_enabled_state {} + update { + if { [llength $update_clauses] > 0 } { + db_dml update_action " + update workflow_fsm_actions + set [join $update_clauses ", "] + where action_id = :action_id + " + } } - unset row(enabled_state_ids) + delete { + # Handled through cascading delete + } } - - # Record where the action is both enabled and assigned - if { [info exists row(assigned_states)] } { - set assigned_p "t" - db_dml delete_enabled_states {} - foreach state_short_name $row(assigned_states) { - set enabled_state_id [workflow::state::fsm::get_id \ - -workflow_id $workflow_id \ - -short_name $state_short_name] - db_dml insert_enabled_state {} + + # Auxilliary rows + switch $operation { + insert - update { + # Record in which states the action is enabled but not assigned + if { [info exists aux(enabled_state_ids)] } { + set assigned_p "f" + db_dml delete_enabled_states {} + foreach enabled_state_id $aux(enabled_state_ids) { + db_dml insert_enabled_state {} + } + unset aux(enabled_state_ids) + } + + # Record where the action is both enabled and assigned + if { [info exists aux(assigned_state_ids)] } { + set assigned_p "t" + db_dml delete_enabled_states {} + foreach enabled_state_id $aux(assigned_state_ids) { + db_dml insert_enabled_state {} + } + unset aux(assigned_state_ids) + } } - unset row(assigned_states) - } elseif { [info exists row(assigned_state_ids)] } { - set assigned_p "t" - db_dml delete_enabled_states {} - foreach enabled_state_id $row(assigned_state_ids) { - db_dml insert_enabled_state {} - } - unset row(assigned_state_ids) } - - # This will error if there are attributes it doesn't know about, so we remove the attributes we know above - workflow::action::edit \ - -internal \ - -action_id $action_id \ - -workflow_id $workflow_id \ - -array row if { !$internal_p } { workflow::definition_changed_handler -workflow_id $workflow_id } } if { !$internal_p } { - # Flush the workflow cache, as changing an action changes the entire workflow - # e.g. initial_action_p, enabled_in_states. workflow::flush_cache -workflow_id $workflow_id } + + return $action_id } - ad_proc -public workflow::action::fsm::delete { {-action_id:required} } { Delete FSM action with given id. @author Peter Marklund } { - db_dml delete_action { - delete from workflow_actions - where action_id = :action_id - } + workflow::action::fsm::edit -operation delete -action_id $action_id } ad_proc -public workflow::action::fsm::get_new_state { @@ -896,25 +1045,27 @@ } # Get the info from the spec - array set action $spec + foreach { key value } $spec { + set action($key) [string trim $value] + } # Create the action set action_id [workflow::action::fsm::new \ - -workflow_id $workflow_id \ - -short_name $short_name \ - -pretty_name $action(pretty_name) \ - -pretty_past_tense $action(pretty_past_tense) \ - -edit_fields $action(edit_fields) \ - -allowed_roles $action(allowed_roles) \ - -assigned_role $action(assigned_role) \ - -privileges $action(privileges) \ - -always_enabled_p $action(always_enabled_p) \ - -enabled_states $action(enabled_states) \ - -assigned_states $action(assigned_states) \ - -new_state $action(new_state) \ - -callbacks $action(callbacks) \ - -initial_action_p $action(initial_action_p) - ] + -workflow_id $workflow_id \ + -short_name $short_name \ + -pretty_name $action(pretty_name) \ + -pretty_past_tense $action(pretty_past_tense) \ + -edit_fields $action(edit_fields) \ + -allowed_roles $action(allowed_roles) \ + -assigned_role $action(assigned_role) \ + -privileges $action(privileges) \ + -always_enabled_p $action(always_enabled_p) \ + -enabled_states $action(enabled_states) \ + -assigned_states $action(assigned_states) \ + -new_state $action(new_state) \ + -callbacks $action(callbacks) \ + -initial_action_p $action(initial_action_p) + ] } ad_proc -private workflow::action::fsm::parse_actions_spec { Index: openacs-4/packages/workflow/tcl/role-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/role-procs.tcl,v diff -u -N -r1.13 -r1.14 --- openacs-4/packages/workflow/tcl/role-procs.tcl 11 Dec 2003 15:30:37 -0000 1.13 +++ openacs-4/packages/workflow/tcl/role-procs.tcl 16 Dec 2003 18:18:49 -0000 1.14 @@ -18,57 +18,9 @@ # ##### -ad_proc -private workflow::role::insert { - {-workflow_id:required} - {-short_name {}} - {-pretty_name:required} - {-sort_order {}} -} { - Inserts the DB row for a new role. You shouldn't normally be usin - this procedure, use workflow::role::new instead. - - @param workflow_id The ID of the workflow the new role belongs to - - @param short_name The short_name of the new role - - @param pretty_name The pretty name of the new role - - @param sort_order The number which this role should be in the sort ordering sequence. - Leave blank to add role at the end. If you provide a sort_order number - which already exists, existing roles are pushed down one number. - - @return The ID of the new role - - @author Lars Pind (lars@collaboraid.biz) - @see workflow::role::new -} { - db_transaction { - - if { [empty_string_p $sort_order] } { - set sort_order [workflow::default_sort_order \ - -workflow_id $workflow_id \ - -table_name "workflow_roles"] - } else { - workflow::role::update_sort_order \ - -workflow_id $workflow_id \ - -sort_order $sort_order - } - - set short_name [workflow::role::generate_short_name \ - -workflow_id $workflow_id \ - -pretty_name $pretty_name \ - -short_name $short_name] - - set role_id [db_nextval "workflow_roles_seq"] - - db_dml do_insert {} - } - - return $role_id -} - ad_proc -public workflow::role::new { {-workflow_id:required} + {-role_id {}} {-short_name {}} {-pretty_name:required} {-sort_order {}} @@ -85,136 +37,219 @@ @author Peter Marklund @author Lars Pind (lars@collaboraid.biz) } { - db_transaction { - # Insert the role - set role_id [insert \ - -workflow_id $workflow_id \ - -short_name $short_name \ - -pretty_name $pretty_name \ - -sort_order $sort_order \ - ] + # Wrapper for workflow::role::edit - # Callbacks - set edit_array(callbacks) $callbacks - workflow::role::edit \ - -internal \ - -workflow_id $workflow_id \ - -role_id $role_id \ - -array edit_array + foreach elm { short_name pretty_name sort_order callbacks } { + set row($elm) [set $elm] } - # Role info for the workflow is changed, need to flush - workflow::role::flush_cache -workflow_id $workflow_id + set role_id [workflow::role::edit \ + -operation "insert" \ + -role_id $role_id \ + -workflow_id $workflow_id \ + -array row] return $role_id } ad_proc -public workflow::role::edit { - {-role_id:required} + {-operation "update"} + {-role_id {}} {-workflow_id {}} - {-array:required} + {-array {}} {-internal:boolean} } { - Edit a workflow role. Attributes of the array are: short_name, pretty_name, sort_order, callbacks. + Edit a workflow role. - @param role_id The role to edit. + Attributes of the array are: - @param workflow_id Optionally specify the workflow_id. If not specified, we will execute a query to find it. - - @param array Name of an array in the caller's namespace with attributes to edit. + short_name + pretty_name + sort_order + callbacks. - @param internal Set this flag if you're calling this proc from within the corresponding proc - for a particular workflow model. Will cause this proc to not flush the cache - or call workflow::definition_changed_handler, which the caller must then do. + @param operation insert, update, delete + @param role_id For update/delete: The role to update or delete. + For insert: Optionally specify a pre-generated role_id for the role. + + @param workflow_id For update/delete: Optionally specify the workflow_id. If not specified, we will execute a query to find it. + For insert: The workflow_id of the new role. + + @param array For insert/update: Name of an array in the caller's namespace with attributes to insert/update. + @return role_id @see workflow::role::new @author Peter Marklund @author Lars Pind (lars@collaboraid.biz) } { - upvar 1 $array row - foreach name [array names row] { - set missing_elm($name) 1 + switch $operation { + update - delete { + if { [empty_string_p $role_id] } { + error "You must specify the role_id of the role to $operation." + } + } + insert {} + default { + error "Illegal operation '$operation'" + } } - - set set_clauses [list] - - if { [empty_string_p $workflow_id] } { - set workflow_id [workflow::role::get_element \ - -role_id $role_id \ - -element workflow_id] + switch $operation { + insert - update { + upvar 1 $array row + if { ![array exists row] } { + error "Array $array does not exist or is not an array" + } + foreach name [array names row] { + set missing_elm($name) 1 + } + } } + switch $operation { + insert { + if { [empty_string_p $workflow_id] } { + error "You must supply workflow_id" + } + # Default sort_order + if { ![exists_and_not_null row(sort_order)] } { + set row(sort_order) [workflow::default_sort_order \ + -workflow_id $workflow_id \ + -table_name "workflow_roles"] + } + # Default short_name on insert + if { ![info exists row(short_name)] } { + set row(short_name) {} + } + } + update { + if { [empty_string_p $workflow_id] } { + set workflow_id [workflow::role::get_element \ + -role_id $role_id \ + -element workflow_id] + } + } + } - # Handle columns in the workflow_roles table - foreach attr { - short_name pretty_name sort_order - } { - if { [info exists row($attr)] } { - set varname attr_$attr + # Parse column values + switch $operation { + insert - update { + set update_clauses [list] + set insert_names [list] + set insert_values [list] - # Convert the Tcl value to something we can use in the query - switch $attr { - short_name { - if { ![exists_and_not_null row(pretty_name)] } { - if { [empty_string_p $row(short_name)] } { - error "You cannot edit with an empty short_name without also setting pretty_name" - } else { - set row(pretty_name) {} + # Handle columns in the workflow_roles table + foreach attr { + short_name pretty_name sort_order + } { + if { [info exists row($attr)] } { + set varname attr_$attr + # Convert the Tcl value to something we can use in the query + switch $attr { + short_name { + if { ![exists_and_not_null row(pretty_name)] } { + if { [empty_string_p $row(short_name)] } { + error "You cannot $operation with an empty short_name without also setting pretty_name" + } else { + set row(pretty_name) {} + } + } + + set $varname [workflow::role::generate_short_name \ + -workflow_id $workflow_id \ + -pretty_name $row(pretty_name) \ + -short_name $row(short_name) \ + -role_id $role_id] } + default { + set $varname $row($attr) + } } - - set $varname [workflow::role::generate_short_name \ - -workflow_id $workflow_id \ - -pretty_name $row(pretty_name) \ - -short_name $row(short_name) \ - -role_id $role_id] + # Add the column to the insert/update statement + switch $attr { + default { + lappend update_clauses "$attr = :$varname" + lappend insert_names $attr + lappend insert_values :$varname + } + } + if { [info exists missing_elm($attr)] } { + unset missing_elm($attr) + } } - default { - set $varname $row($attr) - } } - - lappend set_clauses "$attr = :$varname" - unset missing_elm($attr) } } - + db_transaction { - - if { [info exists row(sort_order)] } { - workflow::role::update_sort_order \ - -workflow_id $workflow_id \ - -sort_order $row(sort_order) + # Sort_order + switch $operation { + insert - update { + if { [info exists row(sort_order)] } { + workflow::role::update_sort_order \ + -workflow_id $workflow_id \ + -sort_order $row(sort_order) + } + } } - - # Update role - if { [llength $set_clauses] > 0 } { - db_dml update_role " - update workflow_roles - set [join $set_clauses ", "] - where role_id = :role_id - " - } - - # Callbacks - if { [info exists row(callbacks)] } { - db_dml delete_callbacks { - delete from workflow_role_callbacks - where role_id = :role_id + # Do the insert/update/delete + switch $operation { + insert { + if { [empty_string_p $role_id] } { + set role_id [db_nextval "workflow_roles_seq"] + } + + lappend insert_names role_id + lappend insert_values :role_id + lappend insert_names workflow_id + lappend insert_values :workflow_id + + db_dml insert_role " + insert into workflow_roles + ([join $insert_names ", "]) + values + ([join $insert_values ", "]) + " } - foreach callback_name $row(callbacks) { - workflow::role::callback_insert \ - -role_id $role_id \ - -name $callback_name + update { + if { [llength $update_clauses] > 0 } { + db_dml update_role " + update workflow_roles + set [join $update_clauses ", "] + where role_id = :role_id + " + } } - unset missing_elm(callbacks) + delete { + db_dml delete_role { + delete from workflow_roles + where role_id = :role_id + } + } } - # Check that there are no unknown attributes - if { [llength [array names missing_elm]] > 0 } { - error "Trying to set illegal action attributes: [join [array names row] ", "]" + switch $operation { + insert - update { + # Callbacks + if { [info exists row(callbacks)] } { + db_dml delete_callbacks { + delete from workflow_role_callbacks + where role_id = :role_id + } + foreach callback_name $row(callbacks) { + workflow::role::callback_insert \ + -role_id $role_id \ + -name $callback_name + } + unset missing_elm(callbacks) + } + + # Check that there are no unknown attributes + if { [llength [array names missing_elm]] > 0 } { + error "Trying to set illegal role attributes: [join [array names missing_elm] ", "]" + } + } } if { !$internal_p } { @@ -223,7 +258,9 @@ } if { !$internal_p } { - workflow::role::flush_cache -workflow_id $workflow_id + # Flush the workflow cache, as changing an role changes the entire workflow + # e.g. initial_role_p, enabled_in_states. + workflow::flush_cache -workflow_id $workflow_id } return $role_id @@ -236,10 +273,9 @@ @author Peter Marklund } { - db_dml delete_role { - delete from workflow_roles - where role_id = :role_id - } + workflow::role::edit \ + -operation "delete" \ + -role_id $role_id } ad_proc -public workflow::role::get_options { @@ -380,7 +416,9 @@ array set role { callbacks {} } # Get the info from the spec - array set role $spec + foreach { key value } $spec { + set role($key) [string trim $value] + } # Create the role set role_id [workflow::role::new \ Index: openacs-4/packages/workflow/tcl/state-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/state-procs.tcl,v diff -u -N -r1.8 -r1.9 --- openacs-4/packages/workflow/tcl/state-procs.tcl 15 Dec 2003 12:14:48 -0000 1.8 +++ openacs-4/packages/workflow/tcl/state-procs.tcl 16 Dec 2003 18:18:49 -0000 1.9 @@ -28,16 +28,16 @@ @param workflow_id The id of the FSM workflow to add the state to - @param short_name If you leave blank, the short_name will be generated from pretty_name. + @param short_name If you leave blank, the short_name will be generated from pretty_name. - @param pretty_name + @param pretty_name - @param hide_fields A space-separated list of the names of form fields which should be - hidden when in this state, because they're irrelevant in a certain state. + @param hide_fields A space-separated list of the names of form fields which should be + hidden when in this state, because they're irrelevant in a certain state. - @param sort_order The number which this state should be in the sort ordering sequence. - Leave blank to add state at the end. If you provide a sort_order number - which already exists, existing states are pushed down one number. + @param sort_order The number which this state should be in the sort ordering sequence. + Leave blank to add state at the end. If you provide a sort_order number + which already exists, existing states are pushed down one number. @param internal Set this flag if you're calling this proc from within the corresponding proc for a particular workflow model. Will cause this proc to not flush the cache @@ -47,136 +47,214 @@ @author Peter Marklund } { - db_transaction { - - set state_id [db_nextval "workflow_fsm_states_seq"] - - if { [empty_string_p $sort_order] } { - set sort_order [workflow::default_sort_order \ - -workflow_id $workflow_id \ - -table_name "workflow_fsm_states"] - } else { - workflow::state::fsm::update_sort_order \ - -workflow_id $workflow_id \ - -sort_order $sort_order - } + # Wrapper for workflow::state::fsm::edit - set short_name [workflow::state::fsm::generate_short_name \ - -workflow_id $workflow_id \ - -pretty_name $pretty_name \ - -short_name $short_name] - - db_dml do_insert {} - - if { !$internal_p } { - workflow::definition_changed_handler -workflow_id $workflow_id - } + foreach elm { short_name pretty_name sort_order } { + set row($elm) [set $elm] } - if { !$internal_p } { - workflow::flush_cache -workflow_id $workflow_id - } - + set state_id [workflow::state::fsm::edit \ + -operation "insert" \ + -workflow_id $workflow_id \ + -array row] + return $state_id } - ad_proc -public workflow::state::fsm::edit { - {-state_id:required} - {-array:required} + {-operation "update"} + {-state_id {}} {-workflow_id {}} + {-array {}} {-internal:boolean} } { - Creates a new state for a certain FSM (Finite State Machine) workflow. - - @param state_id The id of the FSM state you wish to edit + Edit a workflow state. - @param workflow_id Optionally specify the workflow_id. If not specified, we will execute a query to find it. - - @param array Name of an array in the caller's namespace with attributes to edit. + Attributes of the array are: - @param internal Set this flag if you're calling this proc from within the corresponding proc - for a particular workflow model. Will cause this proc to not flush the cache - or call workflow::definition_changed_handler, which the caller must then do. + short_name + pretty_name + sort_order + hide_fields - @return ID of the state. + @param operation insert, update, delete + + @param state_id For update/delete: The state to update or delete. + For insert: Optionally specify a pre-generated state_id for the state. + + @param workflow_id For update/delete: Optionally specify the workflow_id. If not specified, we will execute a query to find it. + For insert: The workflow_id of the new state. + @param array For insert/update: Name of an array in the caller's namespace with attributes to insert/update. + + @return state_id + + @see workflow::state::new + @author Peter Marklund + @author Lars Pind (lars@collaboraid.biz) } { - upvar 1 $array row - if { ![array exists row] } { - error "Array $array does not exist or is not an array" + switch $operation { + update - delete { + if { [empty_string_p $state_id] } { + error "You must specify the state_id of the state to $operation." + } + } + insert {} + default { + error "Illegal operation '$operation'" + } } - foreach name [array names row] { - set missing_elm($name) 1 + switch $operation { + insert - update { + upvar 1 $array row + if { ![array exists row] } { + error "Array $array does not exist or is not an array" + } + foreach name [array names row] { + set missing_elm($name) 1 + } + } } - - if { [empty_string_p $workflow_id] } { - set workflow_id [workflow::state::fsm::get_element \ - -state_id $state_id \ - -element workflow_id] + switch $operation { + insert { + if { [empty_string_p $workflow_id] } { + error "You must supply workflow_id" + } + # Default sort_order + if { ![exists_and_not_null row(sort_order)] } { + set row(sort_order) [workflow::default_sort_order \ + -workflow_id $workflow_id \ + -table_name "workflow_fsm_states"] + } + # Default short_name on insert + if { ![info exists row(short_name)] } { + set row(short_name) {} + } + } + update { + if { [empty_string_p $workflow_id] } { + set workflow_id [workflow::state::fsm::get_element \ + -state_id $state_id \ + -element workflow_id] + } + } } - set set_clauses [list] + # Parse column values + switch $operation { + insert - update { + set update_clauses [list] + set insert_names [list] + set insert_values [list] - # Handle columns in the workflow_fsm_states table - foreach attr { - short_name pretty_name hide_fields sort_order - } { - if { [info exists row($attr)] } { - set varname attr_$attr - - # Convert the Tcl value to something we can use in the query - switch $attr { - short_name { - if { ![exists_and_not_null row(pretty_name)] } { - if { [empty_string_p $row(short_name)] } { - error "You cannot edit with an empty short_name without also setting pretty_name" - } else { - set row(pretty_name) {} + # Handle columns in the workflow_fsm_states table + foreach attr { + short_name pretty_name hide_fields sort_order + } { + if { [info exists row($attr)] } { + set varname attr_$attr + # Convert the Tcl value to something we can use in the query + switch $attr { + short_name { + if { ![exists_and_not_null row(pretty_name)] } { + if { [empty_string_p $row(short_name)] } { + error "You cannot edit with an empty short_name without also setting pretty_name" + } else { + set row(pretty_name) {} + } + } + + set $varname [workflow::state::fsm::generate_short_name \ + -workflow_id $workflow_id \ + -pretty_name $row(pretty_name) \ + -short_name $row(short_name) \ + -state_id $state_id] } + default { + set $varname $row($attr) + } } - - set $varname [workflow::state::fsm::generate_short_name \ - -workflow_id $workflow_id \ - -pretty_name $row(pretty_name) \ - -short_name $row(short_name) \ - -state_id $state_id] + # Add the column to the insert/update statement + switch $attr { + default { + lappend update_clauses "$attr = :$varname" + lappend insert_names $attr + lappend insert_values :$varname + } + } + if { [info exists missing_elm($attr)] } { + unset missing_elm($attr) + } } - default { - set $varname $row($attr) - } } - - # Add the column to the SET clause - lappend set_clauses "$attr = :$varname" - - unset missing_elm($attr) } } - - db_transaction { - # Update state - if { [llength $set_clauses] > 0 } { - db_dml update_action " - update workflow_fsm_states - set [join $set_clauses ", "] - where state_id = :state_id - " + db_transaction { + # Sort_order + switch $operation { + insert - update { + if { [info exists row(sort_order)] } { + workflow::state::fsm::update_sort_order \ + -workflow_id $workflow_id \ + -sort_order $row(sort_order) + } + } } + # Do the insert/update/delete + switch $operation { + insert { + if { [empty_string_p $state_id] } { + set state_id [db_nextval "workflow_fsm_states_seq"] + } - # Check that there are no unknown attributes - if { [llength [array names missing_elm]] > 0 } { - error "Trying to set illegal state attributes: [join [array names missing_elm] ", "]" + lappend insert_names state_id + lappend insert_values :state_id + lappend insert_names workflow_id + lappend insert_values :workflow_id + + db_dml insert_state " + insert into workflow_fsm_states + ([join $insert_names ", "]) + values + ([join $insert_values ", "]) + " + } + update { + if { [llength $update_clauses] > 0 } { + db_dml update_state " + update workflow_fsm_states + set [join $update_clauses ", "] + where state_id = :state_id + " + } + } + delete { + db_dml delete_state { + delete from workflow_fsm_states + where state_id = :state_id + } + } } + switch $operation { + insert - update { + # Check that there are no unknown attributes + if { [llength [array names missing_elm]] > 0 } { + error "Trying to set illegal state attributes: [join [array names missing_elm] ", "]" + } + } + } + if { !$internal_p } { workflow::definition_changed_handler -workflow_id $workflow_id } } if { !$internal_p } { + # Flush the workflow cache, as changing an state changes the entire workflow + # e.g. initial_state_p, enabled_in_states. workflow::flush_cache -workflow_id $workflow_id } @@ -348,7 +426,9 @@ } # Get the info from the spec - array set state $spec + foreach { key value } $spec { + set state($key) [string trim $value] + } # Create the state set state_id [workflow::state::fsm::new \ Index: openacs-4/packages/workflow/tcl/workflow-procs-oracle.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/workflow-procs-oracle.xql,v diff -u -N -r1.4 -r1.5 --- openacs-4/packages/workflow/tcl/workflow-procs-oracle.xql 29 Oct 2003 10:47:12 -0000 1.4 +++ openacs-4/packages/workflow/tcl/workflow-procs-oracle.xql 16 Dec 2003 18:18:49 -0000 1.5 @@ -10,6 +10,8 @@ w.object_id, w.package_key, w.object_type, + w.description, + w.description_mime_type, a.short_name as initial_action, a.action_id as initial_action_id from workflows w, @@ -21,18 +23,18 @@ - + begin :1 := workflow.new ( - short_name => :short_name, - pretty_name => :pretty_name, - package_key => :package_key, - object_id => :object_id, - object_type => :object_type, - creation_user => :creation_user, - creation_ip => :creation_ip, - context_id => :context_id + short_name => :attr_short_name, + pretty_name => :attr_pretty_name, + package_key => :attr_package_key, + object_id => :attr_object_id, + object_type => :attr_object_type, + creation_user => :attr_creation_user, + creation_ip => :attr_creation_ip, + context_id => :attr_context_id ); end; Index: openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql,v diff -u -N -r1.6 -r1.7 --- openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql 29 Oct 2003 10:47:12 -0000 1.6 +++ openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql 16 Dec 2003 18:18:49 -0000 1.7 @@ -10,6 +10,8 @@ w.object_id, w.package_key, w.object_type, + w.description, + w.description_mime_type, a.short_name as initial_action, a.action_id as initial_action_id from workflows w left outer join @@ -22,17 +24,17 @@ - + select workflow__new ( - :short_name, - :pretty_name, - :package_key, - :object_id, - :object_type, - :creation_user, - :creation_ip, - :context_id + :attr_short_name, + :attr_pretty_name, + :attr_package_key, + :attr_object_id, + :attr_object_type, + :attr_creation_user, + :attr_creation_ip, + :attr_context_id ); Index: openacs-4/packages/workflow/tcl/workflow-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/workflow-procs.tcl,v diff -u -N -r1.14 -r1.15 --- openacs-4/packages/workflow/tcl/workflow-procs.tcl 11 Dec 2003 15:31:35 -0000 1.14 +++ openacs-4/packages/workflow/tcl/workflow-procs.tcl 16 Dec 2003 18:18:49 -0000 1.15 @@ -53,53 +53,277 @@ @author Peter Marklund } { - # Auditing information for the acs_objects table - if { [ad_conn isconnected] } { - set creation_user [ad_conn user_id] - set creation_ip [ad_conn peeraddr] - } else { - # No HTTP request so we have don't have IP and user info - set creation_user {} - set creation_ip {} + # Wrapper for workflow::edit + + foreach elm { short_name pretty_name package_key object_id object_type callbacks } { + set row($elm) [set $elm] } - # It makes sense that the workflow inherits permissions from the object - # (typically package type or package instance) that sets the scope of the workflow - set context_id $object_id + set workflow_id [workflow::edit \ + -operation "insert" \ + -array row] - db_transaction { + return $workflow_id +} - if { [empty_string_p $object_id] } { - set object_id [db_null] +ad_proc -public workflow::edit { + {-operation "update"} + {-workflow_id {}} + {-array {}} + {-internal:boolean} +} { + Edit a workflow. + + Attributes of the array are: + + short_name + pretty_name + object_id + package_key + object_type + description + description_mime_type + callbacks + context_id + creation_user + creation_ip + + @param operation insert, update, delete + + @param workflow_id For update/delete: The workflow to update or delete. + + @param array For insert/update: Name of an array in the caller's namespace with attributes to insert/update. + + @return workflow_id + + @see workflow::new + + @author Peter Marklund + @author Lars Pind (lars@collaboraid.biz) +} { + switch $operation { + update - delete { + if { [empty_string_p $workflow_id] } { + error "You must specify the workflow_id of the workflow to $operation." + } } + insert {} + default { + error "Illegal operation '$operation'" + } + } + switch $operation { + insert - update { + upvar 1 $array row + if { ![array exists row] } { + error "Array $array does not exist or is not an array" + } + foreach name [array names row] { + set missing_elm($name) 1 + } + } + } + switch $operation { + insert { + # Check that they didn't try to supply a workflow_id + if { [info exists row(workflow_id)] } { + error "Cannot supply a workflow_id when creating" + } + # Default short_name on insert + if { ![info exists row(short_name)] } { + set row(short_name) {} + } + # Default package_key + if { ![info exists row(package_key)] } { + if { [ad_conn isconnected] } { + set row(package_key) [ad_conn package_key] + } + } + # Default creation_user and creation_ip + if { ![info exists row(creation_user)] } { + if { [ad_conn isconnected] } { + set row(creation_user) [ad_conn user_id] + } else { + set row(creation_user) [db_null] + } + } + if { ![info exists row(creation_ip)] } { + if { [ad_conn isconnected] } { + set row(creation_ip) [ad_conn peeraddr] + } else { + set row(creation_ip) [db_null] + } + } + # Default context_id + if { ![info exists row(context_id)] } { + set row(context_id) $row(object_id) + } + # Default object_type + if { ![info exists row(object_type)] } { + set row(object_type) "acs_object" + } + # Check required values + foreach attr { pretty_name package_key object_id } { + if { ![info exists row($attr)] } { + error "$attr is required when creating a new workflow" + } + } + # These are used when validating/generating short_name + set workflow_array(package_key) $row(package_key) + set workflow_array(object_id) $row(object_id) + } + update { + # These are used when validating/generating short_name + if { [info exists row(package_key)] || ![info exists row(object_id)] } { + workflow::get -workflow_id $workflow_id -array workflow_array + } + if { [info exists row(package_key)] } { + set workflow_array(package_key) $row(package_key) + } + if { [info exists row(object_id)] } { + set workflow_array(object_id) $row(object_id) + } + } + } - set short_name [workflow::generate_short_name \ - -package_key $package_key \ - -object_id $object_id \ - -pretty_name $pretty_name \ - -short_name $short_name] - # Insert the workflow - set workflow_id [db_exec_plsql do_insert {}] - - # Callbacks - foreach callback_name $callbacks { - ns_log Debug "callback_name = $callback_name" - workflow::callback_insert \ - -workflow_id $workflow_id \ - -name $callback_name + # Parse column values + switch $operation { + insert - update { + set update_clauses [list] + set insert_names [list] + set insert_values [list] + + # Handle columns in the workflows table + foreach attr { + short_name + pretty_name + object_id + package_key + object_type + description + description_mime_type + creation_user + creation_ip + context_id + } { + if { [info exists row($attr)] } { + set varname attr_$attr + # Convert the Tcl value to something we can use in the query + switch $attr { + short_name { + if { ![exists_and_not_null row(pretty_name)] } { + if { [empty_string_p $row(short_name)] } { + error "You cannot $operation with an empty short_name without also setting pretty_name" + } else { + set row(pretty_name) {} + } + } + + set $varname [workflow::generate_short_name \ + -workflow_id $workflow_id \ + -pretty_name $row(pretty_name) \ + -short_name $row(short_name) \ + -package_key $workflow_array(package_key) \ + -object_id $workflow_array(object_id)] + } + default { + set $varname $row($attr) + } + } + # Add the column to the insert/update statement + switch $attr { + short_name - pretty_name - package_key - object_id - object_type { + switch $operation { + insert { + # Handled by the PL/SQL call + } + update { + lappend update_clauses "$attr = :$varname" + } + } + } + creation_user - creation_ip - context_id { + if { ![string equal $operation insert] } { + error "Cannot update creation_user, creation_ip, context_id" + } + } + default { + lappend update_clauses "$attr = :$varname" + lappend insert_names $attr + lappend insert_values :$varname + } + } + if { [info exists missing_elm($attr)] } { + unset missing_elm($attr) + } + } + } } - - # May need to parse the simple workflow notation - if { [exists_and_not_null workflow] } { - parse_spec -workflow_id $workflow_id -spec $workflow + } + + db_transaction { + # Do the insert/update/delete + switch $operation { + insert { + # Insert the workflow -- uses a PL/SQL call because it's an object + set workflow_id [db_exec_plsql do_insert {}] + + # Deal with attributes not handled by the PL/SQL call + if { [llength $update_clauses] > 0 } { + db_dml update_workflow " + update workflows + set [join $update_clauses ", "] + where workflow_id = :workflow_id + " + } + } + update { + if { [llength $update_clauses] > 0 } { + db_dml update_workflow " + update workflows + set [join $update_clauses ", "] + where workflow_id = :workflow_id + " + } + } + delete { + db_dml delete_workflow { + delete from workflows + where workflow_id = :workflow_id + } + } } + + switch $operation { + insert - update { + # Callbacks + if { [info exists row(callbacks)] } { + db_dml delete_callbacks { + delete from workflow_callbacks + where workflow_id = :workflow_id + } + foreach callback_name $row(callbacks) { + workflow::callback_insert \ + -workflow_id $workflow_id \ + -name $callback_name + } + unset missing_elm(callbacks) + } + + # Check that there are no unknown attributes + if { [llength [array names missing_elm]] > 0 } { + error "Trying to set illegal workflow attributes: [join [array names missing_elm] ", "]" + } + } + } } - # The lookup proc might have cached that there is no workflow - # with the short name of the workflow we have now created so - # we need to flush - util_memoize_flush_regexp {^workflow::get_id_not_cached} + if { !$internal_p } { + # Flush the workflow cache, as changing an workflow changes the entire workflow + # e.g. initial_workflow_p, enabled_in_states. + workflow::flush_cache -workflow_id $workflow_id + } return $workflow_id } @@ -698,7 +922,9 @@ object_type {acs_object} } - array set workflow $spec + foreach { key value } $spec { + set workflow($key) [string trim $value] + } # Override stuff in the spec with stuff provided as an argument here foreach var { package_key object_id } { @@ -708,24 +934,24 @@ } set workflow_id [workflow::new \ - -short_name $short_name \ - -pretty_name $workflow(pretty_name) \ - -package_key $workflow(package_key) \ - -object_id $object_id \ - -object_type $workflow(object_type) \ - -callbacks $workflow(callbacks)] + -short_name $short_name \ + -pretty_name $workflow(pretty_name) \ + -package_key $workflow(package_key) \ + -object_id $object_id \ + -object_type $workflow(object_type) \ + -callbacks $workflow(callbacks)] workflow::role::parse_roles_spec \ - -workflow_id $workflow_id \ - -spec $workflow(roles) - + -workflow_id $workflow_id \ + -spec $workflow(roles) + workflow::state::fsm::parse_states_spec \ - -workflow_id $workflow_id \ - -spec $workflow(states) - + -workflow_id $workflow_id \ + -spec $workflow(states) + workflow::action::fsm::parse_actions_spec \ - -workflow_id $workflow_id \ - -spec $workflow(actions) + -workflow_id $workflow_id \ + -spec $workflow(actions) return $workflow_id } Index: openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl,v diff -u -N -r1.12 -r1.13 --- openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl 18 Nov 2003 17:57:57 -0000 1.12 +++ openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl 16 Dec 2003 18:18:49 -0000 1.13 @@ -116,6 +116,7 @@ pretty_name "#acs-subsite.About_You#" package_key "acs-automated-testing" object_type "acs_object" + description_mime_type "text/plain" roles { short_name { pretty_name "#acs-subsite.Bad_Password#" @@ -146,6 +147,7 @@ pretty_name "Bug Test" package_key "acs-automated-testing" object_type "acs_object" + description_mime_type "text/plain" roles { submitter { pretty_name "Submitter" @@ -462,9 +464,10 @@ set generated_spec [workflow::fsm::generate_spec -workflow_id $workflow_id] - aa_true "Checking that generated spec 1 is identical to the spec that we created from (except for ordering)" \ - [array_lists_equal_p $generated_spec [workflow_get_array_style_spec]] - + if { ![aa_true "Checking that generated spec 1 is identical to the spec that we created from (except for ordering)" \ + [array_lists_equal_p $generated_spec [workflow_get_array_style_spec]]] } { + ns_log Error "Workflow test case failed: \nDesired spec: [workflow_get_array_style_spec]\n\nActual spec:\n\n[util::array_list_spec_pretty $generated_spec]" + } # Create the workflow case in open state set object_id [workflow::test::workflow_object_id] @@ -717,8 +720,10 @@ set generated_spec [workflow::fsm::generate_spec -workflow_id $workflow_id] - aa_true "Checking that generated spec 2 is identical to the spec that we created from (except for ordering)" \ - [array_lists_equal_p $generated_spec [workflow::test::get_message_key_spec]] + if { ![aa_true "Checking that generated spec 2 is identical to the spec that we created from (except for ordering)" \ + [array_lists_equal_p $generated_spec [workflow::test::get_message_key_spec]]] } { + ns_log Error "Workflow test case failed: \nDesired spec: [workflow::test::get_message_key_spec]\n\nActual spec:\n\n[util::array_list_spec_pretty $generated_spec]" + } } set teardown_chunk {