- Publicity: Public Only All
workflow-procs.tcl
Procedures in the workflow namespace.
- Location:
- packages/workflow/tcl/workflow-procs.tcl
- Created:
- 8 January 2003
- Authors:
- Lars Pind <lars@collaboraid.biz>
- Peter Marklund <peter@collaboraid.biz>
- CVS Identification:
$Id: workflow-procs.tcl,v 1.36 2019/02/12 18:45:14 hectorr Exp $
Procedures in this file
- workflow::cache_timeout (private)
- workflow::callback_insert (private)
- workflow::clone (public)
- workflow::default_sort_order (private)
- workflow::definition_changed_handler (public)
- workflow::delete (public)
- workflow::edit (public)
- workflow::exists_p (public)
- workflow::flush_cache (private)
- workflow::fsm::clone (public)
- workflow::fsm::edit (public)
- workflow::fsm::generate_spec (public)
- workflow::fsm::get_initial_state (public)
- workflow::fsm::get_states (public)
- workflow::fsm::new_from_spec (public)
- workflow::generate_short_name (public)
- workflow::generate_spec (public)
- workflow::get (public)
- workflow::get_actions (public)
- workflow::get_callbacks (private)
- workflow::get_element (public)
- workflow::get_existing_short_names (public)
- workflow::get_id (public)
- workflow::get_id_not_cached (private)
- workflow::get_not_cached (private)
- workflow::get_notification_links (public)
- workflow::get_roles (public)
- workflow::new (public)
- workflow::new_from_spec (public)
- workflow::parse_spec (private)
- workflow::service_contract::action_side_effect (public)
- workflow::service_contract::activity_log_format_title (public)
- workflow::service_contract::get_impl_id (public)
- workflow::service_contract::notification_info (public)
- workflow::service_contract::role_assignee_pick_list (public)
- workflow::service_contract::role_assignee_subquery (public)
- workflow::service_contract::role_default_assignees (public)
Detailed information
workflow::cache_timeout (private)
workflow::cache_timeout
Returns the timeout to give to util_memoize (max_age parameter) for all workflow level data. Should probably be an APM parameter.
- Author:
- Peter Marklund
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::callback_insert (private)
workflow::callback_insert -workflow_id workflow_id -name name \ [ -sort_order sort_order ]
Add a side-effect to a workflow.
- Switches:
- -workflow_id (required)
- The ID of the workflow.
- -name (required)
- Name of service contract implementation, in the form (impl_owner_name).(impl_name), for example, bug-tracker.FormatLogTitle.
- -sort_order (optional)
- The sort_order for the rule. Leave blank to add to the end of the list
- Author:
- Lars Pind <lars@collaboraid.biz>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::clone (public)
workflow::clone -workflow_id workflow_id [ -package_key package_key ] \ [ -object_id object_id ] [ -array array ] \ [ -workflow_handler workflow_handler ]
Clones an existing FSM workflow. The clone must belong to either a package key or an object id.
- Switches:
- -workflow_id (required)
- -package_key (optional)
- A package to which this workflow belongs
- -object_id (optional)
- The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application.
- -array (optional)
- The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned.
- -workflow_handler (optional, defaults to
"workflow"
)- Author:
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::default_sort_order (private)
workflow::default_sort_order -workflow_id workflow_id \ -table_name table_name
By default the sort_order will be the highest current sort order plus 1. This reflects the order in which states and actions are added to the workflow starting with 1
- Switches:
- -workflow_id (required)
- -table_name (required)
- Author:
- Peter Marklund
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::definition_changed_handler (public)
workflow::definition_changed_handler -workflow_id workflow_id
Should be called when the workflow definition has changed while there are active cases. Will update the record of enabled actions in each of the case, so they reflect the new workflow.
- Switches:
- -workflow_id (required)
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::delete (public)
workflow::delete -workflow_id workflow_id
Delete a generic workflow and all data attached to it (states, actions etc.).
- Switches:
- -workflow_id (required)
- The id of the workflow to delete.
- Author:
- Peter Marklund
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::edit (public)
workflow::edit [ -operation operation ] [ -workflow_id workflow_id ] \ [ -array array ] [ -internal ] [ -no_complain ]
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
- Switches:
- -operation (optional, defaults to
"update"
)- insert, update, delete
- -workflow_id (optional)
- For update/delete: The workflow to update or delete.
- -array (optional)
- For insert/update: Name of an array in the caller's namespace with attributes to insert/update.
- -internal (optional, boolean)
- 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.
- -no_complain (optional, boolean)
- Silently ignore extra attributes that we don't know how to handle.
- Returns:
- workflow_id
- Authors:
- Peter Marklund
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::exists_p (public)
workflow::exists_p -workflow_id workflow_id
Return 1 if the workflow with given id exists and 0 otherwise. This proc is currently not cached.
- Switches:
- -workflow_id (required)
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::flush_cache (private)
workflow::flush_cache -workflow_id workflow_id
Flush all cached data related to the given workflow instance.
- Switches:
- -workflow_id (required)
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::fsm::clone (public)
workflow::fsm::clone -workflow_id workflow_id \ [ -package_key package_key ] [ -object_id object_id ] \ [ -array array ]
Clones an existing FSM workflow. The clone must belong to either a package key or an object id.
- Switches:
- -workflow_id (required)
- -package_key (optional)
- A package to which this workflow belongs
- -object_id (optional)
- The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application.
- -array (optional)
- The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned.
- Author:
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::fsm::edit (public)
workflow::fsm::edit [ -operation operation ] \ [ -workflow_id workflow_id ] [ -array array ] [ -internal ]
- Switches:
- -operation (optional, defaults to
"update"
)- -workflow_id (optional)
- -array (optional)
- -internal (optional, boolean)
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::fsm::generate_spec (public)
workflow::fsm::generate_spec -workflow_id workflow_id \ [ -workflow_handler workflow_handler ] [ -handlers handlers ]
Generate a spec for a workflow in array list style.
- Switches:
- -workflow_id (required)
- The id of the workflow to generate a spec for.
- -workflow_handler (optional, defaults to
"workflow"
)- -handlers (optional, defaults to
" roles workflow::role actions workflow::action::fsm states workflow::state::fsm "
)- Returns:
- The spec for the workflow.
- Author:
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::fsm::get_initial_state (public)
workflow::fsm::get_initial_state -workflow_id workflow_id
Get the id of the state that a workflow case is in once it's started (after the initial action is fired).
- Switches:
- -workflow_id (required)
- Author:
- Peter Marklund
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::fsm::get_states (public)
workflow::fsm::get_states [ -all ] -workflow_id workflow_id \ [ -parent_action_id parent_action_id ]
Get the state_id's of all the states in the workflow.
- Switches:
- -all (optional, boolean)
- -workflow_id (required)
- The ID of the workflow
- -parent_action_id (optional)
- Returns:
- list of state_id's.
- Author:
- Lars Pind <lars@collaboraid.biz>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::fsm::new_from_spec (public)
workflow::fsm::new_from_spec [ -package_key package_key ] \ [ -object_id object_id ] -spec spec [ -array array ] \ [ -workflow_handler workflow_handler ] [ -handlers handlers ]
Create a new workflow from spec. Workflows must belong to either a package key or an object id.
- Switches:
- -package_key (optional)
- A package to which this workflow belongs
- -object_id (optional)
- The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application.
- -spec (required)
- The workflow spec
- -array (optional)
- The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned.
- -workflow_handler (optional)
- -handlers (optional)
- Returns:
- The ID of the workflow created
- Author:
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::generate_short_name (public)
workflow::generate_short_name -package_key package_key \ [ -object_id object_id ] -pretty_name pretty_name \ [ -short_name short_name ] [ -workflow_id workflow_id ]
Generate a unique short_name from pretty_name, or verify uniqueness of a given short_name.
- Switches:
- -package_key (required)
- -object_id (optional)
- -pretty_name (required)
- -short_name (optional)
- Suggested short_name.
- -workflow_id (optional)
- If you pass in this, we will allow that workflow's short_name to be reused.
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::generate_spec (public)
workflow::generate_spec -workflow_id workflow_id \ [ -workflow_handler workflow_handler ] [ -handlers handlers ]
Generate a spec for a workflow in array list style. Note that calling this directly with the default arguments will bomb, because workflow::action doesn't implement the required API.
- Switches:
- -workflow_id (required)
- The id of the workflow to generate a spec for.
- -workflow_handler (optional, defaults to
"workflow"
)- -handlers (optional, defaults to
" roles workflow::role actions workflow::action "
)- An array-list with Tcl namespaces where handlers for various elements are defined. The keys are identical to the keys in the spec, and the namespaces are where the procs to handle them are defined.
- Returns:
- The spec for the workflow.
- Author:
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get (public)
workflow::get -workflow_id workflow_id -array array
Return information about a workflow. Uses util_memoize to cache values from the database.
- Switches:
- -workflow_id (required)
- ID of workflow
- -array (required)
- name of array in which the info will be returned
- Returns:
- An array list with keys workflow_id, short_name, pretty_name, object_id, package_key, object_type, and callbacks.
- Author:
- Lars Pind <lars@collaboraid.biz>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_actions (public)
workflow::get_actions [ -all ] -workflow_id workflow_id \ [ -parent_action_id parent_action_id ]
Get the action_id's of all the actions in the workflow.
- Switches:
- -all (optional, boolean)
- -workflow_id (required)
- The ID of the workflow
- -parent_action_id (optional)
- Returns:
- list of action_id's.
- Author:
- Lars Pind <lars@collaboraid.biz>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_callbacks (private)
workflow::get_callbacks -workflow_id workflow_id \ -contract_name contract_name
Return the implementation names for a certain contract and a given workflow.
- Switches:
- -workflow_id (required)
- -contract_name (required)
- Author:
- Peter Marklund
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_element (public)
workflow::get_element -workflow_id workflow_id -element element
Return a single element from the information about a workflow.
- Switches:
- -workflow_id (required)
- The ID of the workflow
- -element (required)
- Returns:
- The element you asked for
- Author:
- Lars Pind <lars@collaboraid.biz>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_existing_short_names (public)
workflow::get_existing_short_names -package_key package_key \ [ -object_id object_id ] \ [ -ignore_workflow_id ignore_workflow_id ]
Returns a list of existing workflow short_names for this package_key and object_id. Useful when you're trying to ensure a short_name is unique, or construct a new short_name that is guaranteed to be unique.
- Switches:
- -package_key (required)
- -object_id (optional)
- -ignore_workflow_id (optional)
- If specified, the short_name for the given workflow will not be included in the result set.
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_id (public)
workflow::get_id [ -package_key package_key ] [ -object_id object_id ] \ -short_name short_name
Get workflow_id by short_name and object_id. Provide either package_key or object_id.
- Switches:
- -package_key (optional)
- The key of the package workflow belongs to.
- -object_id (optional)
- The ID of the object the workflow's for (typically a package instance)
- -short_name (required)
- the short name of the workflow you want
- Returns:
- The id of the workflow or the empty string if no workflow was found.
- Author:
- Lars Pind <lars@collaboraid.biz>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_id_not_cached (private)
workflow::get_id_not_cached [ -package_key package_key ] \ [ -object_id object_id ] -short_name short_name
Private proc not to be used by applications, use workflow::get_id instead.
- Switches:
- -package_key (optional)
- -object_id (optional)
- -short_name (required)
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_not_cached (private)
workflow::get_not_cached -workflow_id workflow_id
Private procedure that should never be used by application code - use workflow::get instead. Returns info about the workflow in an array list. Always goes to the database.
- Switches:
- -workflow_id (required)
- Author:
- Peter Marklund
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_notification_links (public)
workflow::get_notification_links -workflow_id workflow_id \ [ -case_id case_id ] [ -return_url return_url ]
Return a links to sign up for notifications.
- Switches:
- -workflow_id (required)
- -case_id (optional)
- -return_url (optional)
- Returns:
- A multirow with columns url, label, title
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::get_roles (public)
workflow::get_roles [ -all ] -workflow_id workflow_id \ [ -parent_action_id parent_action_id ]
Get the role_id's of all the roles in the workflow.
- Switches:
- -all (optional, boolean)
- -workflow_id (required)
- The ID of the workflow
- -parent_action_id (optional)
- Returns:
- list of role_id's.
- Author:
- Lars Pind <lars@collaboraid.biz>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::new (public)
workflow::new -pretty_name pretty_name [ -short_name short_name ] \ -package_key package_key [ -object_id object_id ] \ [ -object_type object_type ] [ -callbacks callbacks ]
Creates a new workflow. For each workflow you must create an initial action (using the workflow::action::new proc) to be fired when a workflow case is opened.
- Switches:
- -pretty_name (required)
- A human readable name for the workflow for use in the UI.
- -short_name (optional)
- For referring to the workflow from Tcl code. Use Tcl variable syntax.
- -package_key (required)
- The package to which this workflow belongs
- -object_id (optional)
- The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application.
- -object_type (optional, defaults to
"acs_object"
)- The type of objects that the workflow will be applied to. Valid values are in the acs_object_types table. The parameter is optional and defaults to acs_object.
- -callbacks (optional)
- List of names of service contract implementations of callbacks for the workflow in impl_owner_name.impl_name format.
- Returns:
- New workflow_id.
- Author:
- Peter Marklund
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::new_from_spec (public)
workflow::new_from_spec [ -package_key package_key ] \ [ -object_id object_id ] -spec spec [ -array array ] \ [ -workflow_handler workflow_handler ] [ -handlers handlers ]
Create a new workflow from spec. Workflows must belong to either a package key or an object id.
- Switches:
- -package_key (optional)
- A package to which this workflow belongs
- -object_id (optional)
- The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application.
- -spec (required)
- The workflow spec
- -array (optional)
- The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned.
- -workflow_handler (optional, defaults to
"workflow"
)- -handlers (optional, defaults to
" roles workflow::role actions workflow::action "
)- Returns:
- The ID of the workflow created
- Author:
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::parse_spec (private)
workflow::parse_spec -short_name short_name \ [ -package_key package_key ] [ -object_id object_id ] -spec spec \ [ -workflow_handler workflow_handler ] [ -handlers handlers ]
Create workflow, roles, states, actions, etc., as appropriate
- Switches:
- -short_name (required)
- -package_key (optional)
- -object_id (optional)
- -spec (required)
- The roles spec
- -workflow_handler (optional, defaults to
"workflow"
)- -handlers (optional, defaults to
" roles workflow::role actions workflow::action "
)- Author:
- Lars Pind <lars@collaboraid.biz>
- See Also:
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::service_contract::action_side_effect (public)
workflow::service_contract::action_side_effect
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::service_contract::activity_log_format_title (public)
workflow::service_contract::activity_log_format_title
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::service_contract::get_impl_id (public)
workflow::service_contract::get_impl_id -name name
- Switches:
- -name (required)
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::service_contract::notification_info (public)
workflow::service_contract::notification_info
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::service_contract::role_assignee_pick_list (public)
workflow::service_contract::role_assignee_pick_list
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::service_contract::role_assignee_subquery (public)
workflow::service_contract::role_assignee_subquery
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
workflow::service_contract::role_default_assignees (public)
workflow::service_contract::role_default_assignees
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
Content File Source
ad_library { Procedures in the workflow namespace. @creation-date 8 January 2003 @author Lars Pind (lars@collaboraid.biz) @author Peter Marklund (peter@collaboraid.biz) @cvs-id $Id: workflow-procs.tcl,v 1.36 2019/02/12 18:45:14 hectorr Exp $ } namespace eval workflow {} namespace eval workflow::fsm {} namespace eval workflow::service_contract {} ##### # # workflow namespace # ##### d_proc -public workflow::new { {-pretty_name:required} {-short_name {}} {-package_key:required} {-object_id {}} {-object_type "acs_object"} {-callbacks {}} } { Creates a new workflow. For each workflow you must create an initial action (using the workflow::action::new proc) to be fired when a workflow case is opened. @param short_name For referring to the workflow from Tcl code. Use Tcl variable syntax. @param pretty_name A human readable name for the workflow for use in the UI. @param package_key The package to which this workflow belongs @param object_id The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application. @param object_type The type of objects that the workflow will be applied to. Valid values are in the acs_object_types table. The parameter is optional and defaults to acs_object. @param callbacks List of names of service contract implementations of callbacks for the workflow in impl_owner_name.impl_name format. @return New workflow_id. @author Peter Marklund } { # Wrapper for workflow::edit foreach elm { short_name pretty_name package_key object_id object_type callbacks } { set row($elm) [set $elm] } set workflow_id [workflow::edit \ -operation "insert" \ -array row] return $workflow_id } d_proc -public workflow::edit { {-operation "update"} {-workflow_id {}} {-array {}} {-internal:boolean} {-no_complain:boolean} } { Edit a workflow. Attributes of the array are: <ul> <li>short_name <li>pretty_name <li>object_id <li>package_key <li>object_type <li>description <li>description_mime_type <li>callbacks <li>context_id <li>creation_user <li>creation_ip </ul> @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. @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 no_complain Silently ignore extra attributes that we don't know how to handle. @return workflow_id @see workflow::new @author Peter Marklund @author Lars Pind (lars@collaboraid.biz) } { switch $operation { update - delete { if { $workflow_id eq "" } { 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) "" } } if { ![info exists row(creation_ip)] } { if { [ad_conn isconnected] } { set row(creation_ip) [ad_conn peeraddr] } else { set row(creation_ip) "" } } # 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" } } # Default context_id if { ![info exists row(context_id)] } { set row(context_id) $row(object_id) } # 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) } } } # 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 { (![info exists row(pretty_name)] || $row(pretty_name) eq "") } { if { $row(short_name) eq "" } { 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 { $operation ne "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) } } } } } 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 { [array size missing_elm] > 0 && !$no_complain_p } { error "Trying to set illegal workflow attributes: [join [array names missing_elm] ", "]" } } } } 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 } d_proc -public workflow::exists_p { {-workflow_id:required} } { Return 1 if the workflow with given id exists and 0 otherwise. This proc is currently not cached. } { return [db_string do_select {} } d_proc -public workflow::delete { {-workflow_id:required} } { Delete a generic workflow and all data attached to it (states, actions etc.). @param workflow_id The id of the workflow to delete. @author Peter Marklund } { workflow::flush_cache -workflow_id $workflow_id return [db_exec_plsql do_delete {}] } d_proc -public workflow::get_id { {-package_key {}} {-object_id {}} {-short_name:required} } { Get workflow_id by short_name and object_id. Provide either package_key or object_id. @param object_id The ID of the object the workflow's for (typically a package instance) @param package_key The key of the package workflow belongs to. @param short_name the short name of the workflow you want @return The id of the workflow or the empty string if no workflow was found. @author Lars Pind (lars@collaboraid.biz) } { set workflow_id [util_memoize [list workflow::get_id_not_cached \ -package_key $package_key \ -object_id $object_id \ -short_name $short_name] [workflow::cache_timeout]] return $workflow_id } d_proc -public workflow::get { {-workflow_id:required} {-array:required} } { Return information about a workflow. Uses util_memoize to cache values from the database. @author Lars Pind (lars@collaboraid.biz) @param workflow_id ID of workflow @param array name of array in which the info will be returned @return An array list with keys workflow_id, short_name, pretty_name, object_id, package_key, object_type, and callbacks. } { # Select the info into the upvar'ed Tcl Array upvar $array row array set row \ [util_memoize [list workflow::get_not_cached -workflow_id $workflow_id] [workflow::cache_timeout]] } d_proc -public workflow::get_element { {-workflow_id:required} {-element:required} } { Return a single element from the information about a workflow. @param workflow_id The ID of the workflow @return The element you asked for @author Lars Pind (lars@collaboraid.biz) } { get -workflow_id $workflow_id -array row return $row($element) } d_proc -public workflow::get_roles { {-all:boolean} {-workflow_id:required} {-parent_action_id {}} } { Get the role_id's of all the roles in the workflow. @param workflow_id The ID of the workflow @return list of role_id's. @author Lars Pind (lars@collaboraid.biz) } { return [workflow::role::get_ids -all=$all_p -workflow_id $workflow_id -parent_action_id $parent_action_id] } d_proc -public workflow::get_actions { {-all:boolean} {-workflow_id:required} {-parent_action_id {}} } { Get the action_id's of all the actions in the workflow. @param workflow_id The ID of the workflow @return list of action_id's. @author Lars Pind (lars@collaboraid.biz) } { return [workflow::action::get_ids -all=$all_p -workflow_id $workflow_id -parent_action_id $parent_action_id] } d_proc -public workflow::definition_changed_handler { {-workflow_id:required} } { Should be called when the workflow definition has changed while there are active cases. Will update the record of enabled actions in each of the case, so they reflect the new workflow. } { workflow::flush_cache -workflow_id $workflow_id set case_ids [db_list select_cases { select case_id from workflow_cases where workflow_id = :workflow_id }] foreach case_id $case_ids { workflow::case::state_changed_handler \ -case_id $case_id } } d_proc -public workflow::get_existing_short_names { {-package_key:required} {-object_id {}} {-ignore_workflow_id {}} } { Returns a list of existing workflow short_names for this package_key and object_id. Useful when you're trying to ensure a short_name is unique, or construct a new short_name that is guaranteed to be unique. @param ignore_workflow_id If specified, the short_name for the given workflow will not be included in the result set. } { set result [list] db_foreach select_workflows { select workflow_id, short_name from workflows where package_key = :package_key and object_id = :object_id } { if { $ignore_workflow_id eq "" || $ignore_workflow_id ne $workflow_id } { lappend result $short_name } } return $result } d_proc -public workflow::generate_short_name { {-package_key:required} {-object_id {}} {-pretty_name:required} {-short_name {}} {-workflow_id {}} } { Generate a unique short_name from pretty_name, or verify uniqueness of a given short_name. @param workflow_id If you pass in this, we will allow that workflow's short_name to be reused. @param short_name Suggested short_name. } { set existing_short_names [workflow::get_existing_short_names \ -package_key $package_key \ -object_id $object_id \ -ignore_workflow_id $workflow_id] if { $short_name eq "" } { if { $pretty_name eq "" } { error "Cannot have empty pretty_name when short_name is empty" } set short_name [util_text_to_url \ -replacement "_" \ -existing_urls $existing_short_names \ -text $pretty_name] } else { # Make lowercase, remove illegal characters set short_name [string tolower $short_name] regsub -all {[- ]} $short_name {_} short_name regsub -all {[^a-zA-Z_0-9]} $short_name {} short_name if {$short_name in $existing_short_names} { error "Workflow with short_name '$short_name' already exists for this package_key and object_id." } } return $short_name } d_proc -public workflow::generate_spec { {-workflow_id:required} {-workflow_handler "workflow"} {-handlers { roles workflow::role actions workflow::action }} } { Generate a spec for a workflow in array list style. Note that calling this directly with the default arguments will bomb, because workflow::action doesn't implement the required API. @param workflow_id The id of the workflow to generate a spec for. @param handlers An array-list with Tcl namespaces where handlers for various elements are defined. The keys are identical to the keys in the spec, and the namespaces are where the procs to handle them are defined. @return The spec for the workflow. @author Lars Pind (lars@collaboraid.biz) @see workflow::new } { workflow::get -workflow_id $workflow_id -array row set short_name $row(short_name) array unset row object_id array unset row workflow_id array unset row short_name array unset row callbacks_array array unset row callback_ids array unset row callback_impl_names array unset row initial_action array unset row initial_action_id if { (![info exists row(description)] || $row(description) eq "") } { array unset row description_mime_type } set spec [list] # Output sorted, and with no empty elements foreach name [lsort [array names row]] { if { $row($name) ne "" } { lappend spec $name $row($name) } } foreach { key namespace } $handlers { set subspec [list] foreach sub_id [${namespace}::get_ids -workflow_id $workflow_id] { set sub_short_name [${namespace}::get_element \ -one_id $sub_id \ -element short_name] set elm_spec [${namespace}::generate_spec -one_id $sub_id -handlers $handlers] lappend subspec $sub_short_name $elm_spec } lappend spec $key $subspec } set spec [list $short_name $spec] return $spec } d_proc -public workflow::clone { {-workflow_id:required} {-package_key {}} {-object_id {}} {-array {}} {-workflow_handler workflow} } { Clones an existing FSM workflow. The clone must belong to either a package key or an object id. @param pretty_name A human readable name for the workflow for use in the UI. @param object_id The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application. @param package_key A package to which this workflow belongs @param array The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned. @author Lars Pind (lars@collaboraid.biz) @see workflow::new } { if { $array ne "" } { upvar 1 $array row set array row } set spec [${workflow_handler}::generate_spec \ -workflow_id $workflow_id \ -workflow_handler $workflow_handler] set workflow_id [${workflow_handler}::new_from_spec \ -package_key $package_key \ -object_id $object_id \ -spec $spec \ -array $array] return $workflow_id } d_proc -public workflow::new_from_spec { {-package_key {}} {-object_id {}} {-spec:required} {-array {}} {-workflow_handler workflow} {-handlers { roles workflow::role actions workflow::action }} } { Create a new workflow from spec. Workflows must belong to either a package key or an object id. @param package_key A package to which this workflow belongs @param object_id The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application. @param spec The workflow spec @param array The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned. @return The ID of the workflow created @author Lars Pind (lars@collaboraid.biz) @see workflow::new } { if { [llength $spec] > 2 } { # Create any additional (child) workflows first, so they're available when creating the main one below # Not passing in the array, not keeping the workflow_id ${workflow_handler}::new_from_spec \ -package_key $package_key \ -object_id $object_id \ -spec [lrange $spec 2 end] \ -workflow_handler $workflow_handler \ -handlers $handlers } set short_name [lindex $spec 0] array set workflow_array [lindex $spec 1] # Override workflow attributes from the array if { $array ne "" } { upvar 1 $array row foreach name [array names row] { if {$name eq "short_name"} { set short_name $row($name) } else { set workflow_array($name) $row($name) } } } set workflow_id [workflow::parse_spec \ -package_key $package_key \ -object_id $object_id \ -short_name $short_name \ -spec [array get workflow_array] \ -workflow_handler $workflow_handler \ -handlers $handlers] # 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} return $workflow_id } d_proc -private workflow::parse_spec { {-short_name:required} {-package_key {}} {-object_id {}} {-spec:required} {-workflow_handler workflow} {-handlers { roles workflow::role actions workflow::action }} } { Create workflow, roles, states, actions, etc., as appropriate @param workflow_id The id of the workflow to delete. @param spec The roles spec @author Lars Pind (lars@collaboraid.biz) @see workflow::new } { # Default values array set workflow { callbacks {} object_type {acs_object} } foreach { key value } $spec { set workflow($key) [string trim $value] } # Override stuff in the spec with stuff provided as an argument here foreach var { short_name package_key object_id } { if { [set $var] ne "" || (![info exists workflow($var)] || $workflow($var) eq "") } { set workflow($var) [set $var] } } # Pull out the extra types, roles/actions/states, so we don't try to create the workflow with them array set aux [list] array set counter [list] array set remain [list] foreach { key namespace } $handlers { if { [info exists workflow($key)] } { set aux($key) $workflow($key) if { [info exists count($key)] } { incr remain($key) } else { set remain($key) 1 } set counter($key) 0 unset workflow($key) } } array set sub_id [list] db_transaction { # Create the workflow set workflow_id [${workflow_handler}::edit \ -internal \ -operation "insert" \ -array workflow] # Create roles/actions/states foreach { type namespace } $handlers { # type is 'roles', 'actions', 'states', etc. if { [info exists aux($type)] } { incr remain($type) -1 incr counter($type) foreach { subshort_name subspec } $aux($type) { # subshort_name is the short_name of a single role/action/state array unset row array set row $subspec set row(short_name) $subshort_name # string trim everything foreach key [array names row] { set row($key) [string trim $row($key)] } set cmd [list ${namespace}::edit \ -internal \ -workflow_id $workflow_id \ -handlers $handlers \ -array row] if { $counter($type) == 1 } { lappend cmd -operation insert } else { lappend cmd -[string range $type 0 end-1]_id $sub_id(${type},${subshort_name}) } if { $remain($type) == 0 } { lappend cmd -no_complain } set sub_id(${type},${subshort_name}) [eval $cmd] # Flush the cache after all creates workflow::flush_cache -workflow_id $workflow_id } } } } return $workflow_id } #---------------------------------------------------------------------- # Private procs #---------------------------------------------------------------------- d_proc -private workflow::flush_cache { {-workflow_id:required} } { Flush all cached data related to the given workflow instance. } { # The workflow instance that we are flushing may be in the get_id lookup # cache so we have to flush it util_memoize_flush_regexp {^workflow::get_id_not_cached} # Flush workflow scalar attributes and workflow callbacks util_memoize_flush [list workflow::get_not_cached -workflow_id $workflow_id] # Delegating flushing of info related to roles, actions, and states workflow::role::flush_cache -workflow_id $workflow_id workflow::action::flush_cache -workflow_id $workflow_id workflow::state::flush_cache -workflow_id $workflow_id # Flush all workflow cases from the cache. We are flushing more than needed here # but this approach seems easier and faster than looping over a potentially big number # of cases mapped to the workflow in the database, only a few of which may actually be # cached and need flushing workflow::case::flush_cache } ad_proc -private workflow::cache_timeout {} { Returns the timeout to give to util_memoize (max_age parameter) for all workflow level data. Should probably be an APM parameter. @author Peter Marklund } { return "" } d_proc -private workflow::get_id_not_cached { {-package_key {}} {-object_id {}} {-short_name:required} } { Private proc not to be used by applications, use workflow::get_id instead. } { if { $package_key eq "" } { if { $object_id eq "" } { if { [ad_conn isconnected] } { set package_key [ad_conn package_key] set query_name select_workflow_id_by_package_key } else { error "You must supply either package_key or object_id, or there must be a current connection" } } else { set query_name select_workflow_id_by_object_id } } else { if { $object_id eq "" } { set query_name select_workflow_id_by_package_key } else { error "You must supply only one of either package_key or object_id" } } return [db_string $query_name {} -default {}] } d_proc -private workflow::get_not_cached { {-workflow_id:required} } { Private procedure that should never be used by application code - use workflow::get instead. Returns info about the workflow in an array list. Always goes to the database. @see workflow::get @author Peter Marklund } { db_1row workflow_info {} -column_array row set callbacks [list] set callback_ids [list] array set callback_impl_names [list] array set callbacks_array [list] db_foreach workflow_callbacks {} -column_array callback_row { lappend callbacks "$callback_row(impl_owner_name).$callback_row(impl_name)" lappend callback_ids $callback_row(impl_id) lappend callback_impl_names($callback_row(contract_name)) $callback_row(impl_name) set callbacks_array($callback_row(impl_id)) [array get callback_row] } set row(callbacks) $callbacks set row(callback_ids) $callback_ids set row(callback_impl_names) [array get callback_impl_names] set row(callbacks_array) [array get callbacks_array] return [array get row] } d_proc -private workflow::default_sort_order { {-workflow_id:required} {-table_name:required} } { By default the sort_order will be the highest current sort order plus 1. This reflects the order in which states and actions are added to the workflow starting with 1 @author Peter Marklund } { set max_sort_order [db_string max_sort_order {} -default 0] return [expr {$max_sort_order + 1}] } d_proc -private workflow::callback_insert { {-workflow_id:required} {-name:required} {-sort_order {}} } { Add a side-effect to a workflow. @param workflow_id The ID of the workflow. @param name Name of service contract implementation, in the form (impl_owner_name).(impl_name), for example, bug-tracker.FormatLogTitle. @param sort_order The sort_order for the rule. Leave blank to add to the end of the list @author Lars Pind (lars@collaboraid.biz) } { db_transaction { # Get the impl_id set acs_sc_impl_id [workflow::service_contract::get_impl_id -name $name] # Get the sort order if { (![info exists sort_order] || $sort_order eq "") } { set sort_order [db_string select_sort_order {}] } # Insert the callback db_dml insert_callback {} } # Flush workflow scalar attributes and workflow callbacks util_memoize_flush [list workflow::get_not_cached -workflow_id $workflow_id] return $acs_sc_impl_id } d_proc -private workflow::get_callbacks { {-workflow_id:required} {-contract_name:required} } { Return the implementation names for a certain contract and a given workflow. @author Peter Marklund } { array set callback_impl_names [workflow::get_element -workflow_id $workflow_id -element callback_impl_names] if { [info exists callback_impl_names($contract_name)] } { return $callback_impl_names($contract_name) } else { return {} } } d_proc -public workflow::get_notification_links { {-workflow_id:required} {-case_id} {-return_url} } { Return a links to sign up for notifications. @return A multirow with columns url, label, title } { } ##### # # workflow::fsm namespace # ##### d_proc -public workflow::fsm::new_from_spec { {-package_key {}} {-object_id {}} {-spec:required} {-array {}} {-workflow_handler {}} {-handlers {}} } { Create a new workflow from spec. Workflows must belong to either a package key or an object id. @param package_key A package to which this workflow belongs @param object_id The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application. @param spec The workflow spec @param array The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned. @return The ID of the workflow created @author Lars Pind (lars@collaboraid.biz) @see workflow::new } { if { $array ne "" } { upvar 1 $array row set array row } return [workflow::new_from_spec \ -package_key $package_key \ -object_id $object_id \ -spec $spec \ -array $array \ -workflow_handler "workflow::fsm" \ -handlers { roles workflow::role actions workflow::action states workflow::state::fsm actions workflow::action::fsm }] } d_proc -public workflow::fsm::clone { {-workflow_id:required} {-package_key {}} {-object_id {}} {-array {}} } { Clones an existing FSM workflow. The clone must belong to either a package key or an object id. @param object_id The id of an ACS Object indicating the scope the workflow. Typically this will be the id of a package type or a package instance but it could also be some other type of ACS object within a package, for example the id of a bug in the Bug Tracker application. @param package_key A package to which this workflow belongs @param array The name of an array in the caller's namespace. Values in this array will override workflow attributes of the workflow being cloned. @author Lars Pind (lars@collaboraid.biz) @see workflow::new } { if { $array ne "" } { upvar 1 $array row set array row } return [workflow::clone \ -workflow_id $workflow_id \ -package_key $package_key \ -object_id $object_id \ -array $array \ -workflow_handler workflow::fsm] return $workflow_id } d_proc -public workflow::fsm::generate_spec { {-workflow_id:required} {-workflow_handler "workflow"} {-handlers { roles workflow::role actions workflow::action::fsm states workflow::state::fsm }} } { Generate a spec for a workflow in array list style. @param workflow_id The id of the workflow to generate a spec for. @return The spec for the workflow. @author Lars Pind (lars@collaboraid.biz) @see workflow::new } { set spec [workflow::generate_spec \ -workflow_id $workflow_id \ -workflow_handler $workflow_handler \ -handlers $handlers] return $spec } d_proc -public workflow::fsm::get_states { {-all:boolean} {-workflow_id:required} {-parent_action_id {}} } { Get the state_id's of all the states in the workflow. @param workflow_id The ID of the workflow @return list of state_id's. @author Lars Pind (lars@collaboraid.biz) } { return [workflow::state::fsm::get_ids -all=$all_p -workflow_id $workflow_id -parent_action_id $parent_action_id] } d_proc -public workflow::fsm::get_initial_state { {-workflow_id:required} } { Get the id of the state that a workflow case is in once it's started (after the initial action is fired). @author Peter Marklund } { set initial_action_id [workflow::get_element \ -workflow_id $workflow_id \ -element initial_action_id] set initial_state [workflow::action::fsm::get_element \ -action_id $initial_action_id \ -element new_state_id] return $initial_state } d_proc -public workflow::fsm::edit { {-operation "update"} {-workflow_id {}} {-array {}} {-internal:boolean} } { if { $array ne "" } { upvar 1 $array row set array row } return [workflow::edit \ -operation $operation \ -workflow_id $workflow_id \ -array $array \ -internal=$internal_p] } ##### # # workflow::service_contract # ##### ad_proc -public workflow::service_contract::role_default_assignees {} { return "workflow.Role_DefaultAssignees" } ad_proc -public workflow::service_contract::role_assignee_pick_list {} { return "workflow.Role_AssigneePickList" } ad_proc -public workflow::service_contract::role_assignee_subquery {} { return "workflow.Role_AssigneeSubQuery" } ad_proc -public workflow::service_contract::action_side_effect {} { return "workflow.Action_SideEffect" } ad_proc -public workflow::service_contract::activity_log_format_title {} { return "workflow.ActivityLog_FormatTitle" } ad_proc -public workflow::service_contract::notification_info {} { return "workflow.NotificationInfo" } d_proc -public workflow::service_contract::get_impl_id { {-name:required} } { set namev [split $name "."] return [acs_sc::impl::get_id -owner [lindex $namev 0] -name [lindex $namev 1]] } # Local variables: # mode: tcl # tcl-indent-level: 4 # indent-tabs-mode: nil # End: