• 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

Detailed information

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
(defaults to "workflow") (optional)
Author:
Lars Pind <lars@collaboraid.biz>
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 workflow::fsm::clone workflow::fsm::clone (public) workflow::clone workflow::clone workflow::fsm::clone->workflow::clone

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):
%3 workflow::action::edit workflow::action::edit (public) workflow::definition_changed_handler workflow::definition_changed_handler workflow::action::edit->workflow::definition_changed_handler workflow::action::fsm::edit workflow::action::fsm::edit (public) workflow::action::fsm::edit->workflow::definition_changed_handler workflow::action::fsm::set_enabled_in_state workflow::action::fsm::set_enabled_in_state (public) workflow::action::fsm::set_enabled_in_state->workflow::definition_changed_handler workflow::install::after_upgrade workflow::install::after_upgrade (private) workflow::install::after_upgrade->workflow::definition_changed_handler workflow::role::edit workflow::role::edit (public) workflow::role::edit->workflow::definition_changed_handler db_list db_list (public) workflow::definition_changed_handler->db_list workflow::case::state_changed_handler workflow::case::state_changed_handler (private) workflow::definition_changed_handler->workflow::case::state_changed_handler workflow::flush_cache workflow::flush_cache (private) workflow::definition_changed_handler->workflow::flush_cache

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):
%3 bug_tracker::bug::instance_workflow_delete bug_tracker::bug::instance_workflow_delete (private) workflow::delete workflow::delete bug_tracker::bug::instance_workflow_delete->workflow::delete bug_tracker::bug::workflow_delete bug_tracker::bug::workflow_delete (private) bug_tracker::bug::workflow_delete->workflow::delete packages/workflow/www/admin/delete-confirm.tcl packages/workflow/ www/admin/delete-confirm.tcl packages/workflow/www/admin/delete-confirm.tcl->workflow::delete workflow::edit workflow::edit (public) workflow::edit->workflow::delete workflow::test::workflow_teardown workflow::test::workflow_teardown (public) workflow::test::workflow_teardown->workflow::delete db_exec_plsql db_exec_plsql (public) workflow::delete->db_exec_plsql workflow::flush_cache workflow::flush_cache (private) workflow::delete->workflow::flush_cache

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
(defaults to "update") (optional)
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
(boolean) (optional)
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
(boolean) (optional)
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):
%3 packages/workflow/www/admin/workflow-ae.tcl packages/workflow/ www/admin/workflow-ae.tcl workflow::edit workflow::edit packages/workflow/www/admin/workflow-ae.tcl->workflow::edit packages/workflow/www/admin/workflow-meta-edit.tcl packages/workflow/ www/admin/workflow-meta-edit.tcl packages/workflow/www/admin/workflow-meta-edit.tcl->workflow::edit workflow::fsm::edit workflow::fsm::edit (public) workflow::fsm::edit->workflow::edit workflow::new workflow::new (public) workflow::new->workflow::edit ad_conn ad_conn (public) workflow::edit->ad_conn db_dml db_dml (public) workflow::edit->db_dml db_exec_plsql db_exec_plsql (public) workflow::edit->db_exec_plsql db_transaction db_transaction (public) workflow::edit->db_transaction workflow::callback_insert workflow::callback_insert (private) workflow::edit->workflow::callback_insert

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):
%3 db_string db_string (public) workflow::exists_p workflow::exists_p workflow::exists_p->db_string

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):
%3 bug_tracker::bug::instance_workflow_create bug_tracker::bug::instance_workflow_create (private) workflow::fsm::clone workflow::fsm::clone bug_tracker::bug::instance_workflow_create->workflow::fsm::clone packages/workflow/www/admin/workflow-clone.tcl packages/workflow/ www/admin/workflow-clone.tcl packages/workflow/www/admin/workflow-clone.tcl->workflow::fsm::clone workflow::clone workflow::clone (public) workflow::fsm::clone->workflow::clone

Testcases:
No testcase defined.

workflow::fsm::edit (public)

 workflow::fsm::edit [ -operation operation ] \
    [ -workflow_id workflow_id ] [ -array array ] [ -internal ]
Switches:
-operation
(defaults to "update") (optional)
-workflow_id
(optional)
-array
(optional)
-internal
(boolean) (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 workflow::edit workflow::edit (public) workflow::fsm::edit workflow::fsm::edit workflow::fsm::edit->workflow::edit

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
(defaults to "workflow") (optional)
-handlers
(defaults to " roles workflow::role actions workflow::action::fsm states workflow::state::fsm ") (optional)
Returns:
The spec for the workflow.
Author:
Lars Pind <lars@collaboraid.biz>
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 workflow::test::run_bug_tracker_test workflow::test::run_bug_tracker_test (public) workflow::fsm::generate_spec workflow::fsm::generate_spec workflow::test::run_bug_tracker_test->workflow::fsm::generate_spec workflow::generate_spec workflow::generate_spec (public) workflow::fsm::generate_spec->workflow::generate_spec

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):
%3 bug_tracker::get_mapped_bugs bug_tracker::get_mapped_bugs (public) workflow::fsm::get_initial_state workflow::fsm::get_initial_state bug_tracker::get_mapped_bugs->workflow::fsm::get_initial_state packages/bug-tracker/lib/nav-bar.tcl packages/bug-tracker/ lib/nav-bar.tcl packages/bug-tracker/lib/nav-bar.tcl->workflow::fsm::get_initial_state packages/bug-tracker/www/map-patch-to-bugs.tcl packages/bug-tracker/ www/map-patch-to-bugs.tcl packages/bug-tracker/www/map-patch-to-bugs.tcl->workflow::fsm::get_initial_state workflow::action::fsm::get_element workflow::action::fsm::get_element (public) workflow::fsm::get_initial_state->workflow::action::fsm::get_element workflow::get_element workflow::get_element (public) workflow::fsm::get_initial_state->workflow::get_element

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
(boolean) (optional)
-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):
%3 bug_tracker::status_get_options bug_tracker::status_get_options (public) workflow::fsm::get_states workflow::fsm::get_states bug_tracker::status_get_options->workflow::fsm::get_states workflow::action::get_all_info_not_cached workflow::action::get_all_info_not_cached (private) workflow::action::get_all_info_not_cached->workflow::fsm::get_states workflow::case::new workflow::case::new (public) workflow::case::new->workflow::fsm::get_states workflow::graph::draw workflow::graph::draw (public) workflow::graph::draw->workflow::fsm::get_states workflow::state::fsm::generate_states_spec workflow::state::fsm::generate_states_spec (private) workflow::state::fsm::generate_states_spec->workflow::fsm::get_states workflow::state::fsm::get_ids workflow::state::fsm::get_ids (private) workflow::fsm::get_states->workflow::state::fsm::get_ids

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):
%3 bug_tracker::bug::workflow_create bug_tracker::bug::workflow_create (private) workflow::fsm::new_from_spec workflow::fsm::new_from_spec bug_tracker::bug::workflow_create->workflow::fsm::new_from_spec workflow::test::workflow_setup_array_style workflow::test::workflow_setup_array_style (public) workflow::test::workflow_setup_array_style->workflow::fsm::new_from_spec workflow::new_from_spec workflow::new_from_spec (public) workflow::fsm::new_from_spec->workflow::new_from_spec

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):
%3 workflow::edit workflow::edit (public) workflow::generate_short_name workflow::generate_short_name workflow::edit->workflow::generate_short_name _ _ (public) workflow::generate_short_name->_ util_text_to_url util_text_to_url (public) workflow::generate_short_name->util_text_to_url workflow::get_existing_short_names workflow::get_existing_short_names (public) workflow::generate_short_name->workflow::get_existing_short_names

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
(defaults to "workflow") (optional)
-handlers
(defaults to " roles workflow::role actions workflow::action ") (optional)
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):
%3 workflow::fsm::generate_spec workflow::fsm::generate_spec (public) workflow::generate_spec workflow::generate_spec workflow::fsm::generate_spec->workflow::generate_spec workflow::get workflow::get (public) workflow::generate_spec->workflow::get

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):
%3 packages/workflow/www/admin/index.tcl packages/workflow/ www/admin/index.tcl workflow::get workflow::get packages/workflow/www/admin/index.tcl->workflow::get packages/workflow/www/admin/workflow-ae.tcl packages/workflow/ www/admin/workflow-ae.tcl packages/workflow/www/admin/workflow-ae.tcl->workflow::get packages/workflow/www/admin/workflow-edit.tcl packages/workflow/ www/admin/workflow-edit.tcl packages/workflow/www/admin/workflow-edit.tcl->workflow::get packages/workflow/www/admin/workflow-meta-edit.tcl packages/workflow/ www/admin/workflow-meta-edit.tcl packages/workflow/www/admin/workflow-meta-edit.tcl->workflow::get workflow::case::action::notify workflow::case::action::notify (public) workflow::case::action::notify->workflow::get util_memoize util_memoize (public) workflow::get->util_memoize workflow::cache_timeout workflow::cache_timeout (private) workflow::get->workflow::cache_timeout workflow::get_not_cached workflow::get_not_cached (private) workflow::get->workflow::get_not_cached

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
(boolean) (optional)
-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):
%3 bug_tracker::bug::get_list bug_tracker::bug::get_list (public) workflow::get_actions workflow::get_actions bug_tracker::bug::get_list->workflow::get_actions bug_tracker::get_export_variables bug_tracker::get_export_variables (public) bug_tracker::get_export_variables->workflow::get_actions bug_tracker::get_page_variables bug_tracker::get_page_variables (public) bug_tracker::get_page_variables->workflow::get_actions workflow::action::get_existing_short_names workflow::action::get_existing_short_names (public) workflow::action::get_existing_short_names->workflow::get_actions workflow::action::get_options workflow::action::get_options (public) workflow::action::get_options->workflow::get_actions workflow::action::get_ids workflow::action::get_ids (public) workflow::get_actions->workflow::action::get_ids

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):
%3 packages/bug-tracker/www/admin/index.tcl packages/bug-tracker/ www/admin/index.tcl workflow::get_element workflow::get_element packages/bug-tracker/www/admin/index.tcl->workflow::get_element workflow::case::get_notification_object workflow::case::get_notification_object (public) workflow::case::get_notification_object->workflow::get_element workflow::case::new workflow::case::new (public) workflow::case::new->workflow::get_element workflow::fsm::get_initial_state workflow::fsm::get_initial_state (public) workflow::fsm::get_initial_state->workflow::get_element workflow::get_callbacks workflow::get_callbacks (private) workflow::get_callbacks->workflow::get_element workflow::get workflow::get (public) workflow::get_element->workflow::get

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):
%3 workflow::generate_short_name workflow::generate_short_name (public) workflow::get_existing_short_names workflow::get_existing_short_names workflow::generate_short_name->workflow::get_existing_short_names db_foreach db_foreach (public) workflow::get_existing_short_names->db_foreach

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):
%3 bug_tracker::bug::get_package_workflow_id bug_tracker::bug::get_package_workflow_id (public) workflow::get_id workflow::get_id bug_tracker::bug::get_package_workflow_id->workflow::get_id bug_tracker::bug::new bug_tracker::bug::new (public) bug_tracker::bug::new->workflow::get_id workflow::test::workflow_id workflow::test::workflow_id (public) workflow::test::workflow_id->workflow::get_id util_memoize util_memoize (public) workflow::get_id->util_memoize workflow::cache_timeout workflow::cache_timeout (private) workflow::get_id->workflow::cache_timeout workflow::get_id_not_cached workflow::get_id_not_cached (private) workflow::get_id->workflow::get_id_not_cached

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):
%3

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
(boolean) (optional)
-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):
%3 bug_tracker::search::bug::datasource bug_tracker::search::bug::datasource (private) workflow::get_roles workflow::get_roles bug_tracker::search::bug::datasource->workflow::get_roles workflow::case::action::notify workflow::case::action::notify (public) workflow::case::action::notify->workflow::get_roles workflow::case::role::add_assignee_widgets workflow::case::role::add_assignee_widgets (public) workflow::case::role::add_assignee_widgets->workflow::get_roles workflow::case::role::set_assignee_values workflow::case::role::set_assignee_values (public) workflow::case::role::set_assignee_values->workflow::get_roles workflow::role::add_assignee_widgets workflow::role::add_assignee_widgets (public) workflow::role::add_assignee_widgets->workflow::get_roles workflow::role::get_ids workflow::role::get_ids (private) workflow::get_roles->workflow::role::get_ids

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
(defaults to "acs_object") (optional)
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):
%3 packages/workflow/www/admin/workflow-ae.tcl packages/workflow/ www/admin/workflow-ae.tcl workflow::new workflow::new packages/workflow/www/admin/workflow-ae.tcl->workflow::new workflow::test::workflow_setup workflow::test::workflow_setup (public) workflow::test::workflow_setup->workflow::new workflow::edit workflow::edit (public) workflow::new->workflow::edit

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
(defaults to "workflow") (optional)
-handlers
(defaults to " roles workflow::role actions workflow::action ") (optional)
Returns:
The ID of the workflow created
Author:
Lars Pind <lars@collaboraid.biz>
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 workflow::fsm::new_from_spec workflow::fsm::new_from_spec (public) workflow::new_from_spec workflow::new_from_spec workflow::fsm::new_from_spec->workflow::new_from_spec util_memoize_flush_regexp util_memoize_flush_regexp (public) workflow::new_from_spec->util_memoize_flush_regexp workflow::parse_spec workflow::parse_spec (private) workflow::new_from_spec->workflow::parse_spec

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):
%3 bug_tracker::install::register_capture_resolution_code_impl bug_tracker::install::register_capture_resolution_code_impl (private) workflow::service_contract::action_side_effect workflow::service_contract::action_side_effect bug_tracker::install::register_capture_resolution_code_impl->workflow::service_contract::action_side_effect bug_tracker::install::unregister_implementations bug_tracker::install::unregister_implementations (private) bug_tracker::install::unregister_implementations->workflow::service_contract::action_side_effect workflow::case::action::do_side_effects workflow::case::action::do_side_effects (public) workflow::case::action::do_side_effects->workflow::service_contract::action_side_effect workflow::install::create_action_side_effect_service_contract workflow::install::create_action_side_effect_service_contract (private) workflow::install::create_action_side_effect_service_contract->workflow::service_contract::action_side_effect workflow::install::delete_service_contracts workflow::install::delete_service_contracts (private) workflow::install::delete_service_contracts->workflow::service_contract::action_side_effect

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):
%3 bug_tracker::install::register_format_log_title_impl bug_tracker::install::register_format_log_title_impl (private) workflow::service_contract::activity_log_format_title workflow::service_contract::activity_log_format_title bug_tracker::install::register_format_log_title_impl->workflow::service_contract::activity_log_format_title bug_tracker::install::unregister_implementations bug_tracker::install::unregister_implementations (private) bug_tracker::install::unregister_implementations->workflow::service_contract::activity_log_format_title workflow::case::get_activity_log_info_not_cached workflow::case::get_activity_log_info_not_cached (private) workflow::case::get_activity_log_info_not_cached->workflow::service_contract::activity_log_format_title workflow::install::create_activity_log_format_title_service_contract workflow::install::create_activity_log_format_title_service_contract (private) workflow::install::create_activity_log_format_title_service_contract->workflow::service_contract::activity_log_format_title workflow::install::delete_service_contracts workflow::install::delete_service_contracts (private) workflow::install::delete_service_contracts->workflow::service_contract::activity_log_format_title

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):
%3 workflow::action::callback_insert workflow::action::callback_insert (public) workflow::service_contract::get_impl_id workflow::service_contract::get_impl_id workflow::action::callback_insert->workflow::service_contract::get_impl_id workflow::callback_insert workflow::callback_insert (private) workflow::callback_insert->workflow::service_contract::get_impl_id workflow::role::callback_insert workflow::role::callback_insert (public) workflow::role::callback_insert->workflow::service_contract::get_impl_id acs_sc::impl::get_id acs_sc::impl::get_id (public) workflow::service_contract::get_impl_id->acs_sc::impl::get_id

Testcases:
No testcase defined.

workflow::service_contract::notification_info (public)

 workflow::service_contract::notification_info

Partial Call Graph (max 5 caller/called nodes):
%3 bug_tracker::install::register_bug_notification_info_impl bug_tracker::install::register_bug_notification_info_impl (private) workflow::service_contract::notification_info workflow::service_contract::notification_info bug_tracker::install::register_bug_notification_info_impl->workflow::service_contract::notification_info bug_tracker::install::unregister_implementations bug_tracker::install::unregister_implementations (private) bug_tracker::install::unregister_implementations->workflow::service_contract::notification_info workflow::case::action::notify workflow::case::action::notify (public) workflow::case::action::notify->workflow::service_contract::notification_info workflow::install::create_get_notification_info_service_contract workflow::install::create_get_notification_info_service_contract (private) workflow::install::create_get_notification_info_service_contract->workflow::service_contract::notification_info workflow::install::delete_service_contracts workflow::install::delete_service_contracts (private) workflow::install::delete_service_contracts->workflow::service_contract::notification_info

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):
%3 workflow::case::role::get_picklist workflow::case::role::get_picklist (public) workflow::service_contract::role_assignee_pick_list workflow::service_contract::role_assignee_pick_list workflow::case::role::get_picklist->workflow::service_contract::role_assignee_pick_list workflow::install::create_assignee_pick_list_service_contract workflow::install::create_assignee_pick_list_service_contract (private) workflow::install::create_assignee_pick_list_service_contract->workflow::service_contract::role_assignee_pick_list workflow::install::delete_service_contracts workflow::install::delete_service_contracts (private) workflow::install::delete_service_contracts->workflow::service_contract::role_assignee_pick_list workflow::install::register_pick_list_current_assignee_impl workflow::install::register_pick_list_current_assignee_impl (private) workflow::install::register_pick_list_current_assignee_impl->workflow::service_contract::role_assignee_pick_list workflow::install::unregister_implementations workflow::install::unregister_implementations (private) workflow::install::unregister_implementations->workflow::service_contract::role_assignee_pick_list

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):
%3 workflow::case::role::get_search_query workflow::case::role::get_search_query (public) workflow::service_contract::role_assignee_subquery workflow::service_contract::role_assignee_subquery workflow::case::role::get_search_query->workflow::service_contract::role_assignee_subquery workflow::install::create_assignee_subquery_service_contract workflow::install::create_assignee_subquery_service_contract (private) workflow::install::create_assignee_subquery_service_contract->workflow::service_contract::role_assignee_subquery workflow::install::delete_service_contracts workflow::install::delete_service_contracts (private) workflow::install::delete_service_contracts->workflow::service_contract::role_assignee_subquery workflow::install::register_search_query_registered_users_impl workflow::install::register_search_query_registered_users_impl (private) workflow::install::register_search_query_registered_users_impl->workflow::service_contract::role_assignee_subquery workflow::install::unregister_implementations workflow::install::unregister_implementations (private) workflow::install::unregister_implementations->workflow::service_contract::role_assignee_subquery

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):
%3 bug_tracker::install::register_component_maintainer_impl bug_tracker::install::register_component_maintainer_impl (private) workflow::service_contract::role_default_assignees workflow::service_contract::role_default_assignees bug_tracker::install::register_component_maintainer_impl->workflow::service_contract::role_default_assignees bug_tracker::install::register_project_maintainer_impl bug_tracker::install::register_project_maintainer_impl (private) bug_tracker::install::register_project_maintainer_impl->workflow::service_contract::role_default_assignees bug_tracker::install::unregister_implementations bug_tracker::install::unregister_implementations (private) bug_tracker::install::unregister_implementations->workflow::service_contract::role_default_assignees workflow::case::role::set_default_assignees workflow::case::role::set_default_assignees (public) workflow::case::role::set_default_assignees->workflow::service_contract::role_default_assignees workflow::install::create_default_assignees_service_contract workflow::install::create_default_assignees_service_contract (private) workflow::install::create_default_assignees_service_contract->workflow::service_contract::role_default_assignees

Testcases:
No testcase defined.
[ hide source ] | [ make this the default ]

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: