Index: openacs-4/packages/workflow/workflow.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/workflow.info,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/workflow.info 10 Jan 2003 13:37:46 -0000 1.1 +++ openacs-4/packages/workflow/workflow.info 14 Jan 2003 15:08:54 -0000 1.2 @@ -30,12 +30,17 @@ + + + + + Index: openacs-4/packages/workflow/sql/postgresql/workflow-procedural-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/postgresql/workflow-procedural-create.sql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/sql/postgresql/workflow-procedural-create.sql 10 Jan 2003 13:38:15 -0000 1.1 +++ openacs-4/packages/workflow/sql/postgresql/workflow-procedural-create.sql 14 Jan 2003 15:09:07 -0000 1.2 @@ -11,13 +11,24 @@ -- Workflow level, Generic Model --------------------------------- +create function workflow__delete (integer) +returns integer as ' +declare + delete_workflow_id alias for $1; +begin + select acs_object__delete(delete_workflow_id); + + return 0; +end;' language 'plpgsql'; + + -- Function for creating a workflow create function workflow__new (varchar, -- short_name varchar, -- pretty_name integer, -- object_id varchar, -- object_type integer, -- creation_user - integer, -- creation_ip + varchar, -- creation_ip integer -- context_id ) returns integer as ' @@ -34,7 +45,7 @@ begin -- Instantiate the ACS Object super type with auditing info v_workflow_id := acs_object__new(null, - ''workflow_new'', + ''workflow_lite'', now(), p_creation_user, p_creation_ip, @@ -45,7 +56,9 @@ insert into workflows (workflow_id, short_name, pretty_name, object_id, object_type) values - (v_workflow_id, p_short_name, p_pretty_name, p_object_id, p_object_type) + (v_workflow_id, p_short_name, p_pretty_name, p_object_id, p_object_type); + + return v_workflow_id; end; ' language 'plpgsql'; Index: openacs-4/packages/workflow/sql/postgresql/workflow-procedural-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/postgresql/workflow-procedural-drop.sql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/sql/postgresql/workflow-procedural-drop.sql 10 Jan 2003 13:38:15 -0000 1.1 +++ openacs-4/packages/workflow/sql/postgresql/workflow-procedural-drop.sql 14 Jan 2003 15:09:07 -0000 1.2 @@ -12,11 +12,12 @@ --------------------------------- -- Drop all functions -drop function workflow__new (varchar, -- short_name +drop function workflow__delete (integer); +drop function workflow__new (varchar, -- short_name varchar, -- pretty_name integer, -- object_id varchar, -- object_type integer, -- creation_user - integer, -- creation_ip + varchar, -- creation_ip integer -- context_id ); Index: openacs-4/packages/workflow/sql/postgresql/workflow-service-contracts-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/postgresql/workflow-service-contracts-create.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/postgresql/workflow-service-contracts-create.sql 14 Jan 2003 15:09:07 -0000 1.1 @@ -0,0 +1,70 @@ +-- +-- Workflow Service Contracts +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @version $Id: workflow-service-contracts-create.sql,v 1.1 2003/01/14 15:09:07 peterm Exp $ +-- +-- GNU GPL v2 +-- + +-- +-- The service contract for workflows +-- + +create function inline_1() +returns integer as ' +DECLARE +BEGIN + PERFORM acs_sc_contract__new ( + ''NotificationType'', + ''Notification Type'' + ); + + PERFORM acs_sc_msg_type__new ( + ''NotificationType.GetURL.InputType'', + ''object_id:integer'' + ); + + PERFORM acs_sc_msg_type__new ( + ''NotificationType.GetURL.OutputType'', + ''url:string'' + ); + + PERFORM acs_sc_operation__new ( + ''NotificationType'', + ''GetURL'', + ''gets the URL for an object in this notification type'', + ''f'', + 1, + ''NotificationType.GetURL.InputType'', + ''NotificationType.GetURL.OutputType'' + ); + + PERFORM acs_sc_msg_type__new ( + ''NotificationType.ProcessReply.InputType'', + ''reply_id:integer'' + ); + + PERFORM acs_sc_msg_type__new ( + ''NotificationType.ProcessReply.OutputType'', + ''success_p:boolean'' + ); + + PERFORM acs_sc_operation__new ( + ''NotificationType'', + ''ProcessReply'', + ''Process a single reply'', + ''f'', + 1, + ''NotificationType.ProcessReply.InputType'', + ''NotificationType.ProcessReply.OutputType'' + ); + + return (0); + +END; +' language 'plpgsql'; + +select inline_1(); +drop function inline_1(); + Index: openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql 10 Jan 2003 13:38:15 -0000 1.1 +++ openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql 14 Jan 2003 15:09:07 -0000 1.2 @@ -14,15 +14,15 @@ --------------------------------- -- Create the workflow object type --- We use workflow_new rather than just workflow +-- We use workflow_lite rather than just workflow -- to avoid a clash with the old workflow package acs-workflow create function inline_0 () returns integer as ' begin PERFORM acs_object_type__create_type ( - ''workflow_new'', - ''New Workflow'', - ''New Workflows'', + ''workflow_lite'', + ''Workflow Lite'', + ''Workflow Lites'', ''acs_object'', ''workflows'', ''workflow_id'', @@ -37,6 +37,11 @@ select inline_0 (); drop function inline_0 (); +-- A generic table for any kind of workflow implementation +-- Currently, the table only holds FSM workflows but when +-- other types of workflows are added we will add a table +-- to hold workflow_types and reference that table from +-- this workflows table. create table workflows ( workflow_id integer constraint workflows_pk @@ -108,9 +113,7 @@ not null ); -create sequence t_wf_workflow_roles_seq; -create view wf_workflow_roles_seq as -select nextval('t_wf_workflow_roles_seq') as nextval; +create sequence workflow_roles_seq; -- Static role-party map create table workflow_role_default_parties ( @@ -195,12 +198,11 @@ assigned_role integer constraint workflow_actions_assigned_role_fk references workflow_roles(role_id) - on delete set null + on delete set null, + always_enabled_p bool default 'f' ); -create sequence t_wf_workflow_actions_seq; -create view wf_workflow_actions_seq as -select nextval('t_wf_workflow_actions_seq') as nextval; +create sequence workflow_actions_seq; -- Determines which roles are allowed to take certain actions create table workflow_action_allowed_roles ( @@ -261,6 +263,20 @@ primary key (action_id, acs_sc_impl_id) ); +create table workflow_initial_action ( + workflow_id integer + constraint workflow_initial_action_pk + primary key + constraint workflow_initial_action_wf_fk + references workflows(workflow_id) + on delete cascade, + action_id integer + constraint workflow_initial_action_act_fk + references workflow_actions(action_id) + on delete cascade +); + + --------------------------------- -- Workflow level, Finite State Machine Model --------------------------------- @@ -278,6 +294,7 @@ sort_order integer constraint workflow_fsm_states_sort_order_nn not null, + -- The state with the lowest sort order is the initial state short_name varchar(100) constraint workflow_fsm_states_short_name_nn not null, @@ -286,24 +303,21 @@ not null ); -create sequence t_wf_workflow_fsm_states_seq; -create view wf_workflow_fsm_states_seq as -select nextval('t_wf_workflow_fsm_states_seq') as nextval; +create sequence workflow_fsm_states_seq; create table workflow_fsm_actions ( action_id integer constraint workflow_fsm_actions_pk - primary key - constraint workflow_fsm_actions_action_id_fk - references workflow_actions(action_id) - on delete cascade, + primary key, new_state integer constraint workflow_fsm_actions_new_state_fk references workflow_fsm_states(state_id) on delete set null -- can be null ); +-- If an action is enabled in all states it won't have any entries in this table +-- it is enabled in all states create table workflow_fsm_action_enabled_in_states ( action_id integer constraint workflow_fsm_action_enabled_in_states_action_id_nn @@ -319,24 +333,12 @@ on delete cascade ); -create table workflow_fsm ( - workflow_id integer - constraint workflow_fsm_pk - primary key - constraint workflow_fsm_workflow_id_fk - references workflows(workflow_id) - on delete cascade, - initial_state integer - constraint workflow_fsm_initial_state_nn - not null - constraint workflow_fsm_initial_state_fk - references workflow_fsm_states(state_id) -); - --------------------------------- -- Case level, Generic Model --------------------------------- +create sequence workflow_cases_seq; + create table workflow_cases ( case_id integer constraint workflow_cases_pk @@ -361,6 +363,8 @@ -- the object which this case is about, e.g. the acs-object for a bug-tracker bug ); +create sequence workflow_case_log_seq; + create table workflow_case_log ( entry_id integer constraint workflow_case_log_pk @@ -435,8 +439,6 @@ references workflow_cases(case_id) on delete cascade, current_state integer - constraint workflow_case_fsm_state_id_nn - not null constraint workflow_case_fsm_state_id_fk references workflow_fsm_states(state_id) on delete cascade Index: openacs-4/packages/workflow/sql/postgresql/workflow-tables-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/postgresql/workflow-tables-drop.sql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/sql/postgresql/workflow-tables-drop.sql 10 Jan 2003 13:38:15 -0000 1.1 +++ openacs-4/packages/workflow/sql/postgresql/workflow-tables-drop.sql 14 Jan 2003 15:09:07 -0000 1.2 @@ -17,7 +17,7 @@ row record; begin for row in select object_id from acs_objects - where object_type = ''workflow_new'' + where object_type = ''workflow_lite'' loop perform acs_object__delete(row.object_id); end loop; @@ -31,7 +31,7 @@ create function inline_0 () returns integer as ' begin - perform acs_object_type__drop_type(''workflow_new'', ''t''); + perform acs_object_type__drop_type(''workflow_lite'', ''t''); return 1; end;' language 'plpgsql'; @@ -44,9 +44,9 @@ drop table workflow_case_log_data; drop table workflow_case_log; drop table workflow_cases; -drop table workflow_fsm; drop table workflow_fsm_action_enabled_in_states; drop table workflow_fsm_actions; +drop table workflow_initial_action; drop table workflow_fsm_states; drop table workflow_action_side_effects; drop table workflow_action_privileges; @@ -59,10 +59,9 @@ drop table workflow_side_effects; drop table workflows; --- Drop all sequences and their views -drop sequence t_wf_workflow_roles_seq; -drop view wf_workflow_roles_seq; -drop sequence t_wf_workflow_actions_seq; -drop view wf_workflow_actions_seq; -drop sequence t_wf_workflow_fsm_states_seq; -drop view wf_workflow_fsm_states_seq; +-- Drop sequences +drop sequence workflow_roles_seq; +drop sequence workflow_actions_seq; +drop sequence workflow_fsm_states_seq; +drop sequence workflow_cases_seq; +drop sequence workflow_case_log_seq; Index: openacs-4/packages/workflow/tcl/action-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/action-procs.tcl,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/action-procs.tcl 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/action-procs.tcl 14 Jan 2003 15:09:16 -0000 1.2 @@ -7,29 +7,41 @@ @cvs-id $Id$ } -namespace eval ::workflow::action {} +namespace eval workflow::action {} +namespace eval workflow:::action::fsm {} -ad_proc -public ::workflow::action::add { +##### +# +# workflow::action namespace +# +##### + +ad_proc -public workflow::action::add { {-workflow_id:required} + {-sort_order {}} {-short_name:required} {-pretty_name:required} {-pretty_past_tense {}} {-assigned_role {}} {-allowed_roles {}} {-privileges {}} + {-always_enabled_p f} } { - This procedure should never be invoked from application code. Instead use + This procedure is normally not invoked from application code. Instead a procedure for a certain workflow implementation, such as for example - workflow::fsm::action::add for Finite State Machine workflows. + workflow::fsm::action::add (for Finite State Machine workflows), is used. @param workflow_id The id of the FSM workflow to add the action to @param short_name Short name of the action for use in source code. Should be on Tcl variable syntax. @param pretty_name Human readable name of the action for use in UI. @param pretty_past_tense Past tense of pretty name - @param assigned_role Users in this role are expected (obliged) to take + @param assigned_role The name of an assigned role. Users in this + role are expected (obliged) to take the action. - @param allowed_roles Users in these roles are allowed to take the action. + @param allowed_roles A list of role names. Users in these roles are + allowed to take the action. + @param privileges Users with these privileges on the object treated by the workflow (i.e. a bug in the Bug Tracker) will be allowed to take this @@ -43,7 +55,16 @@ } { db_transaction { # Insert basic action info - set action_id [db_nextval "wf_workflow_fsm_actions_seq"] + if { [empty_string_p $sort_order] } { + set sort_order [workflow::default_sort_order -workflow_id $workflow_id workflow_actions] + } + set action_id [db_nextval "workflow_actions_seq"] + if { [empty_string_p $assigned_role] } { + set assigned_role_id [db_null] + } else { + set assigned_role_id [workflow::role::get_id -workflow_id $workflow_id \ + -short_name $assigned_role] + } db_dml insert_action {} # Record which roles are allowed to take action @@ -59,3 +80,64 @@ return $action_id } + +ad_proc -public workflow::action::get_assigned_role { + {-action_id:required} +} { + Return the assigned role of the given action + @param action_id The action_id of the action. + @return role_id of the assigned role. +} { + return [db_string select_assigned_role {}] +} + +ad_proc -public workflow::action::get_allowed_roles { + {-action_id:required} +} { + Return the assigned role of the given action + @param action_id The action_id of the action. + @return List of role_id of the allowed roles +} { + return [db_list select_allowed_roles {}] +} + +ad_proc -public workflow::action::get_privileges { + {-action_id:required} +} { + Return the assigned role of the given action + @param action_id The action_id of the action. + @return List of privileges that give permission to do this action +} { + return [db_list select_privileges {}] +} + +ad_proc -public workflow::action::get_id { + {-workflow_id:required} + {-short_name:required} +} { + Return the action_id of the action with the given short_name in the given workflow. + + @param workflow_id The ID of the workflow + @param short_name The short_name of the action + @return action_id of the desired action, or the empty string if it can't be found. +} { + return [db_string select_action_id {} -default {}] +} + + + +##### +# +# workflow::action::fsm +# +##### + +ad_proc -public workflow::action::fsm::get_new_state { + {-action_id:required} +} { + Return the new state for an action + @param action_id The action_id of the action. + @return The new state after executing this action, or the empty string if the action doesn't change the state. +} { + return [db_string select_new_state {} -default {}] +} Index: openacs-4/packages/workflow/tcl/action-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/action-procs.xql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/action-procs.xql 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/action-procs.xql 14 Jan 2003 15:09:16 -0000 1.2 @@ -1,30 +1,78 @@ - + - insert into workflow_actions - (action_id, workflow_id, sort_order, short_name, - pretty_name, pretty_past_tense, assigned_role) - values (:action_id, :workflow_id, :sort_order, :short_name, - :pretty_name, :pretty_past_tense, :assigned_role) - - - - - insert into workflow_action_allowed_roles - (action_id, role_id) - values (:action_id, :allowed_role) + select :action_id, + (select role_id + from workflow_roles + where workflow_id = :workflow_id + and short_name = :allowed_role) as role_id - + insert into workflow_action_privileges (action_id, privilege) values (:action_id, :privilege) + + + select assigned_role + from workflow_actions + where action_id = :action_id + + + + + + select role_id + from workflow_action_allowed_roles + where action_id = :action_id + + + + + + select privilege + from workflow_action_privileges + where action_id = :action_id + + + + + + select action_id + from workflow_actions + where workflow_id = :workflow_id + and short_name = :short_name + + + + + + insert into workflow_actions + select :action_id, + :workflow_id, + :sort_order, + :short_name, + :pretty_name, + :pretty_past_tense, + :assigned_role_id, + :always_enabled_p + + + + + + select new_state + from workflow_fsm_actions a + where action_id = :action_id + + + Index: openacs-4/packages/workflow/tcl/case-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/case-procs-postgresql.xql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/case-procs-postgresql.xql 14 Jan 2003 15:09:16 -0000 1.1 @@ -0,0 +1,15 @@ + + + postgresql7.2 + + + + select state_id + from workflow_fsm_states + where workflow_id = :workflow_id + order by sort_order + limit 1 + + + + Index: openacs-4/packages/workflow/tcl/case-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/case-procs.tcl,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/case-procs.tcl 14 Jan 2003 15:09:16 -0000 1.1 @@ -0,0 +1,474 @@ +ad_library { + Procedures in the case namespace. + + @creation-date 13 January 2003 + @author Lars Pind (lars@collaboraid.biz) + @author Peter Marklund (peter@collaboraid.biz) + @cvs-id $Id: case-procs.tcl,v 1.1 2003/01/14 15:09:16 peterm Exp $ +} + +namespace eval workflow::case {} +namespace eval workflow::case::fsm {} +namespace eval workflow::case::action {} +namespace eval workflow::case::action::fsm {} + +##### +# +# workflow::case +# +##### + +ad_proc -public workflow::case::insert { + {-workflow_id:required} + {-object_id:required} +} { + Start a new case for this workflow and object + + @param case_object_id The object_id which the case is about + @param workflow_short_name The short_name of the workflow. + @return The case_id of the case. Returns the empty string if no case could be found. + + @author Lars Pind (lars@collaboraid.biz) +} { + set case_id [db_nextval "workflow_cases_seq"] + + db_transaction { + # Create the case + db_dml insert_case {} + + # Initialize the FSM state to NULL + db_dml insert_case_fsm {} + } + + return $case_id +} + +ad_proc -public workflow::case::new { + {-workflow_id:required} + {-object_id:required} + {-comment:required} + {-comment_format:required} + {-user_id} +} { + Start a new case for this workflow and object + + @param case_object_id The object_id which the case is about + @param workflow_short_name The short_name of the workflow. + @return The case_id of the case. Returns the empty string if no case could be found. + + @author Lars Pind (lars@collaboraid.biz) +} { + if { ![exists_and_not_null user_id] } { + set user_id [ad_conn user_id] + } + + db_transaction { + # Insert the case + set case_id [insert -workflow_id $workflow_id -object_id $object_id] + + # Execute the initial action + workflow::case::action::execute \ + -case_id $case_id \ + -action_id [get_initial_action -workflow_id $workflow_id] \ + -comment $comment \ + -comment_format $comment_format \ + -user_id $user_id \ + -no_check + } + + return $case_id +} + +ad_proc -public workflow::case::get_case_id { + {-case_object_id:required} + {-workflow_short_name:required} +} { + Gets the case_id from the object_id which the case is about, + along with the short_name of the workflow. + + @param case_object_id The object_id which the case is about + @param workflow_short_name The short_name of the workflow. + @return The case_id of the case. Returns the empty string if no case could be found. + + @author Lars Pind (lars@collaboraid.biz) +} { + set found_p [db_0or1row select_case_id {}] + if { $found_p } { + return $case_id + } else { + return {} + } +} + +ad_proc -public workflow::case::get_object_id { + {-case_id:required} +} { + Gets the object_id from the case. + + @param case_id The case_id. + @return The object_id of the case. + + @author Lars Pind (lars@collaboraid.biz) +} { + return [db_string select_object_id {}] +} + +ad_proc -public workflow::case::get_user_roles { + {-case_id:required} + -user_id +} { + Get the roles which this user is assigned to +of + @param case_id The ID of the case. + @param user_id The user_id of the user for which you want to know the roles. + @return A list of role_id's of the roles which the user is assigned to in this case. + + @author Lars Pind (lars@collaboraid.biz) +} { + if { ![exists_and_not_null user_id] } { + set user_id [ad_conn user_id] + } + return [db_list select_user_roles {}] +} + +ad_proc -public workflow::case::get_enabled_actions { + {-case_id:required} +} { + Get the currently enabled actions, based on the state of the case + + @param case_id The ID of the case. + @return A list of action_id's of the actions which are currently enabled + + @author Lars Pind (lars@collaboraid.biz) +} { + set action_ids [db_list select_enabled_actions {}] + return $action_ids +} + +ad_proc -public workflow::case::get_user_actions { + {-case_id:required} + -user_id +} { + Get the currently enabled actions, which the user has permission + to execute. + + @param case_id The ID of the case. + @return A list of action_id's of the actions which are currently enabled + + @author Lars Pind (lars@collaboraid.biz) +} { + if { ![exists_and_not_null user_id] } { + set user_id [ad_conn user_id] + } + + set action_ids [list] + + foreach action_id [get_enabled_actions -case_id $case_id] { + if { [workflow::case::action::permission_p -case_id $case_id -action_id $action_id -user_id $user_id] } { + lappend action_ids $action_id + } + } + + return $action_ids +} + +ad_proc -public workflow::case::assign_roles { + {-case_id:required} +} { + Find out which roles are assigned to currently enabled actions. + If any of these currently have zero assignees, run the default + assignment process. + + @param case_id the ID of the case. + + @author Lars Pind (lars@collaboraid.biz) +} { + set role_id_list [list] + + foreach action_id [get_enabled_actions -case_id $case_id] { + set role_id [workflow::action::get_assigned_role -action_id $action_id] + if { [lsearch $role_id_list $role_id] == -1 } { + lappend role_id_list $role_id + } + } + + foreach role_id $role_id_list { + set num_assignees [db_string select_num_assignees {}] + + if { $num_assignees == 0 } { + workflow::case::role::set_default_assignees -case_id $case_id -role_id $role_id + } + } +} + + + + + +##### +# +# workflow::case::role namespace +# +##### + +ad_proc -public workflow::case::role::set_default_assignees { + {-case_id:required} + {-role_id:required} +} { + Find the default assignee for this role. + + @param case_id the ID of the case. + @param role_id the ID of the role to assign. + + @author Lars Pind (lars@collaboraid.biz) +} { + set contract_name [workflow::service_contract::role_default_assignee] + + set object_id [workflow::case::get_object_id -case_id $case_id] + + db_foreach select_assignment_rules {} { + + # Run the service contract + set party_id_list [acs_sc_call $contract_name "GetAssignees" [list $case_id $object_id $role_id] $impl_name] + + if { [llength $party_id_list] != 0 } { + foreach party_id $party_id_list { + assignee_insert -case_id $case_id -role_id $role_id -party_id $party_id + } + # We stop when the first callback returned something + break + } + } +} + +ad_proc -public workflow::case::role::assignee_insert { + {-case_id:required} + {-role_id:required} + {-party_id:required} +} { + Insert a new assignee for this role + + @param case_id the ID of the case. + @param role_id the ID of the role to assign. + @param party_id the ID of party to assign to this role + + @author Lars Pind (lars@collaboraid.biz) +} { + if { [catch { + db_dml insert_assignee {} + } errMsg] } { + set already_assigned_p [db_string already_assigned_p {}] + if { !$already_assigned_p } { + global errorInfo errorCode + error $errMsg $errorInfo $errorCode + } + } +} + + + +##### +# +# workflow::case::fsm +# +##### + +ad_proc -public workflow::case::fsm::get_current_state { + {-case_id:required} +} { + Gets the current state_id of this case. + + @param case_id The case_id. + @return The state_id of the state which this case is in + + @author Lars Pind (lars@collaboraid.biz) +} { + return [db_string select_current_state {}] +} + + + + + +##### +# +# workflow::case::action +# +##### + +ad_proc -public workflow::case::action::permission_p { + {-case_id:required} + {-action_id:required} + {-user_id} +} { + Does the user have permission to perform this action. Doesn't + check whether the action is enabled. + + @param case_id The ID of the case. + @param action_id The ID of the action + @param user_id The user. + @return true or false. + + @author Lars Pind (lars@collaboraid.biz) +} { + if { ![exists_and_not_null user_id] } { + set user_id [ad_conn user_id] + } + + set object_id [get_object_id -case_id $case_id] + set user_role_ids [get_user_roles -case_id $case_id -user_id $user_id] + + set permission_p 0 + + foreach role_id $user_role_ids { + + # Is this an assigned role for this action? + set assigned_role [workflow::action::get_assigned_role -action_id $action_id] + if { $assigned_role == $role_id } { + set permission_p 1 + break + } + + # Is this an allowed role for this action? + set allowed_roles [workflow::action::get_allowed_roles -action_id $action_id] + if { [lsearch $allowed_roles $role_id] != -1 } { + set permission_p 1 + break + } + } + + if { !$permission_p } { + set privileges [workflow::action::get_privileges -action_id $action_id] + foreach privilege $privileges { + if { [permission::permission_p -object_id $object_id -privilege $privilege] } { + set permission_p 1 + break + } + } + } + + return $permission_p +} + +ad_proc -public workflow::case::action::enabled_p { + {-case_id:required} + {-action_id:required} +} { + Is this action currently enabled. + + @param case_id The ID of the case. + @param action_id The ID of the action + @return true or false. + + @author Lars Pind (lars@collaboraid.biz) +} { + return [db_string select_enabled_p {} -default 0] +} + +ad_proc -public workflow::case::action::available_p { + {-case_id:required} + {-action_id:required} + {-user_id} +} { + Is this action currently enabled and does the user have permission to perform it? + + @param case_id The ID of the case. + @param action_id The ID of the action + @param user_id The user. + @return true or false. + + @author Lars Pind (lars@collaboraid.biz) +} { + if { ![exists_and_not_null user_id] } { + set user_id [ad_conn user_id] + } + + if { + [enabled_p -case_id $case_id -action_id $action_id] + && + [permission_p -case_id $case_id -action_id $action_id -user_id $user_id] + } { + return 1 + } else { + return 0 + } +} + +ad_proc -public workflow::case::action::execute { + {-case_id:required} + {-action_id:required} + {-comment:required} + {-comment_format:required} + {-user_id} + {-no_check:boolean} +} { + Execute the action + + @param case_id The ID of the case. + @param action_id The ID of the action + @param comment Comment for the case activity log + @param comment_format Format of the comment, according to OpenACS standard text formatting (HM!) + @param user_id User_id + + @author Lars Pind (lars@collaboraid.biz) +} { + if { ![exists_and_not_null user_id] } { + set user_id [ad_conn user_id] + } + + if { !$no_check_p } { + if { ![available_p -case_id $case_id -action_id $action_id -user_id $user_id] } { + error "This user is not allowed to perform this action at this time." + } + } + + set new_state [workflow::action::fsm::get_new_state -action_id $action_id] + + db_transaction { + + # Update the workflow state + if { ![empty_string_p $new_state] } { + db_dml update_fsm_state {} + } + + # Insert activity log entry + set entry_id [db_nextval "workflow_case_log_seq"] + db_dml insert_log_entry {} + + # Assign new enabled roles, if currently unassigned + workflow::case::assign_roles -case_id $case_id + + # Fire side-effects, both action ones and workflow ones + # ... TODO ... + + # Notifications + # ... TODO ... + } +} + + +##### +# +# workflow::case::action::fsm +# +##### + +ad_proc -public workflow::case::action::fsm::new_state { + {-case_id:required} + {-action_id:required} +} { + The new state which the workflow will be in after this action. + + @param case_id The ID of the case. + @param action_id The ID of the action + @return The state_id of the new state which the workflow will be in after this action + + @author Lars Pind (lars@collaboraid.biz) +} { + set new_state [workflow::action::fsm::get_new_state -action_id $action_id] + if { [empty_string_p $new_state] } { + set new_state [workflow::case::fsm::get_current_state -case_id $case_id] + } + return $new_state +} + Index: openacs-4/packages/workflow/tcl/case-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/case-procs.xql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/case-procs.xql 14 Jan 2003 15:09:16 -0000 1.1 @@ -0,0 +1,161 @@ + + + + + + insert into workflow_cases ( + case_id, workflow_id, object_id + ) values ( + :case_id, :workflow_id, :object_id + ) + + + + + + insert into workflow_case_fsm ( + case_id, current_state + ) values ( + :case_id, null + ) + + + + + + select case_id + from workflow_cases c, + workflows w + where c.object_id = :case_object_id + and w.workflow_id = c.workflow_id + and w.short_name = :workflow_short_name + + + + + + select object_id + from workflow_cases c + where c.case_id = :case_id + + + + + + select distinct rpm.role_id + from workflow_case_role_party_map rpm, + party_approved_member_map pmm + where rpm.case_id = :case_id + and rpm.party_id = pmm.party_id + and pmm.member_id = :user_id + + + + + + select distinct action_id + from (select waeis.action_id + from workflow_cases c, + workflow_case_fsm cfsm, + workflow_fsm_action_enabled_in_states waeis + where c.case_id = :case_id + and cfsm.case_id = c.case_id + and waeis.state_id = cfsm.state_id + + union + + select a.action_id + from workflow_cases c, + workflow_actions a + where c.case_id = :case_id + and a.workflow_id = c.workflow_id + and a.always_enabled_p = 't' + ) + + + + + + select count(*) + from workflow_case_role_party_map + where case_id = :case_id + and role_id = :role_id + + + + + + select impl.impl_name + from workflow_role_assignment_rules r, + acs_sc_impls impl, + acs_sc_bindings bind, + acs_sc_contracts ctr + where r.role_id = :role_id + and impl.impl_id = r.acs_sc_impl_id + and impl.impl_id = bind.impl_id + and bind.contract_id = ctr.contract_id + and ctr.contract_name = :contract_name + order by r.sort_order + + + + + + insert into workflow_case_role_party_map + (case_id, role_id, party_id) + values + (:case_id, :role_id, :party_id) + + + + + + select count(*) + from workflow_case_role_party_map + where case_id = :case_id + and role_id = :role_id + and party_id = :party_id + + + + + + select current_state + from workflow_case_fsm c + where c.case_id = :case_id + + + + + + select 1 + from workflow_actions a + where a.action_id = :action_id + and a.always_enabled_p = 't' or + exists (select 1 + from workflow_actions_enabled_in_states waeis, + workflow_cases_fsm c_fsm, + where waeis.action_id = a.action_id + and c_fsm.case_id = :case_id + and waeis.state_id = c_fsm.current_state) + + + + + + update workflow_case_fsm + set current_state = :new_state + where case_id = :case_id + + + + + + insert into workflow_case_log + (entry_id, case_id, action_id, user_id, comment, comment_format) + values + (:entry_id, :case_id, :action_id, :user_id, :comment, :comment_format) + + + + Index: openacs-4/packages/workflow/tcl/fsm-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/Attic/fsm-procs.tcl,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/fsm-procs.tcl 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/fsm-procs.tcl 14 Jan 2003 15:09:16 -0000 1.2 @@ -8,26 +8,36 @@ @cvs-id $Id$ } -namespace eval ::workflow::fsm {} +namespace eval workflow::fsm {} +namespace eval workflow::fsm::state {} +namespace eval workflow::fsm::action {} + +##### +# +# workflow::fsm namespace +# +##### -ad_proc -public ::workflow::fsm::set_initial_state { +ad_proc -public workflow::fsm::delete { {-workflow_id:required} - {-initial_state:required} } { - Set the initial state of an FSM (Finite State Machine) workflow. + Delete an FSM workflow and all data attached to it (states, actions etc.). - @param workflow_id The id of the workflow to set initial state for. - @param initial_state The id of the initial state of the workflow + @param workflow_id The id of the FSM workflow to delete. @author Peter Marklund } { - db_dml do_insert {} + # All FSM data hangs on the generic workflow data and will be deleted on cascade + workflow::delete $workflow_id } +##### +# +# workflow::fsm::state namespace +# +##### -namespace eval ::workflow::fsm::state {} - -ad_proc -public ::workflow::fsm::state::add { +ad_proc -public workflow::fsm::state::add { {-workflow_id:required} {-short_name:required} {-pretty_name:required} @@ -41,26 +51,36 @@ @author Peter Marklund } { - set state_id [db_nextval "wf_workflow_fsm_states_seq"] + set state_id [db_nextval "workflow_fsm_states_seq"] + if { [empty_string_p $sort_order] } { + set sort_order [workflow::default_sort_order -workflow_id $workflow_id workflow_fsm_states] + } + db_dml do_insert {} } -ad_proc -public ::workflow::fsm::state::id_from_short_name { - short_name +ad_proc -public workflow::fsm::state::get_id { + {-workflow_id:required} + {-short_name:required} } { Return the id of the state with given short name + @param workflow_id The id of the workflow the state belongs to. @param short_name The name of the state to return the id for. @author Peter Marklund } { - return [db_string id_from_name {}] + return [db_string select_id {}] } -namespace eval ::workflow::fsm::action {} +##### +# +# workflow::fsm::action namespace +# +##### -ad_proc -public ::workflow::fsm::action::add { +ad_proc -public workflow::fsm::action::add { {-workflow_id:required} {-short_name:required} {-pretty_name:required} @@ -78,7 +98,7 @@ @param enabled_states The short names of states in which the action is enabled. - @param new_state The state that a workflow case moves to + @param new_state The name of the state that a workflow case moves to when the action is taken. Optional. @see workflow::action::add @@ -88,22 +108,29 @@ db_transaction { # Generic workflow data: - set action_id [workflow::action::add -workflow_id $workflow_id - -short_name $short_name - -pretty_name $pretty_name - -pretty_past_tense $pretty_past_tense - -allowed_roles $allowed_roles - -assigned_role $assigned_role + set action_id [workflow::action::add -workflow_id $workflow_id \ + -short_name $short_name \ + -pretty_name $pretty_name \ + -pretty_past_tense $pretty_past_tense \ + -allowed_roles $allowed_roles \ + -assigned_role $assigned_role \ -privileges $privileges] # FSM specific data: # Record whether the action changes state + if { ![empty_string_p $new_state] } { + set new_state_id [workflow::fsm::state::get_id -workflow_id $workflow_id \ + -short_name $new_state] + } else { + set new_state_id [db_null] + } db_dml insert_fsm_action {} # Record in which states the action is enabled foreach state_short_name $enabled_states { - set enabled_state_id [workflow::fsm::state::id_from_short_name $state_short_name] + set enabled_state_id [workflow::fsm::state::get_id -workflow_id $workflow_id \ + -short_name $state_short_name] db_dml insert_enabled_state {} } } Index: openacs-4/packages/workflow/tcl/fsm-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/Attic/fsm-procs.xql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/fsm-procs.xql 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/fsm-procs.xql 14 Jan 2003 15:09:16 -0000 1.2 @@ -1,14 +1,6 @@ - - - insert into workflow_fsm - (workflow_id, initial_state) - values (:workflow_id, :initial_state) - - - insert into workflow_fsm_states @@ -17,20 +9,21 @@ - + select state_id - from workflow workflow_fsm_states + from workflow_fsm_states where short_name = :short_name + and workflow_id = :workflow_id - insert info workflow_fsm_actions + insert into workflow_fsm_actions (action_id, new_state) values - (:action_id, :new_state) + (:action_id, :new_state_id) Index: openacs-4/packages/workflow/tcl/install-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/install-procs.tcl,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/install-procs.tcl 14 Jan 2003 15:09:16 -0000 1.1 @@ -0,0 +1,199 @@ + + # Array-list with list of lists style + + set operations { + GetObjectType { + {operation_desc "Get the object type for which this operation is valid."} + {inputspec {}} + {outputspec {object_type:string}} + {nargs 0} + {iscachable_p "t"} + } + GetPrettyName { + {operation_desc "Get the pretty name. May contain #...#, so should be localized."} + {inputspec {}} + {outputspec {pretty_name:string}} + {nargs 0} + {iscachable_p "t"} + } + GetAssignees { + {operation_desc "Get the assignees as a Tcl list of party_ids, of the default assignees for this case, object, role"} + {inputspec {case_id:integer,object_id:integer,role_id:integer}} + {outputspec {party_ids:[integer]}} + {nargs 3} + {iscachable_p "f"} + } + } + + + +##### +# +# Style one: Lists of lists of lists with potentially unclear semantics +# +##### + +ad_proc -private workflow::install::create_service_contracts {} { + + # Array-list style + +acs_sc::contract::new \ + -contract_name [workflow::service_contract::role_default_assignee] \ + -contract_desc "Service contract to get the default assignees for a role from parameters case_id, object_id and role_id" \ + -operations { + + GetObjectType { + operation_desc "Get the object type for which this operation is valid." + inputspec {} + outputspec {object_type:string} + nargs 0 + iscachable_p "t" + } + + GetPrettyName { + operation_desc "Get the pretty name. May contain #...#, so should be localized." + inputspec {} + outputspec {pretty_name:string} + nargs 0 + iscachable_p "t" + } + + GetAssignees { + operation_desc "Get the assignees as a Tcl list of party_ids, of the default assignees for this case, object, role" + inputspec {case_id:integer,object_id:integer,role_id:integer} + outputspec {party_ids:[integer]} + nargs 3 + iscachable_p "f" + } +} + +} + +##### +# +# Style two: Using procs in procs with global variables +# +##### + +ad_proc -private workflow::install::create_service_contracts {} { + +acs_sc::contract::new \ + -contract_name [workflow::service_contract::role_default_assignee] \ + -contract_desc "Service contract to get the default assignees for a role from parameters case_id, object_id and role_id" \ + -operations { + + operation \ + -operation_name GetObjectType \ + -operation_desc "Get the object type for which this operation is valid." \ + -inputspec {} \ + -outputspec {object_type:string} \ + -nargs 0 \ + -iscachable_p "t" + + operation \ + -operation_name GetPrettyName \ + -operation_desc "Get the pretty name. May contain #...#, so should be localized." \ + -inputspec {} \ + -outputspec {pretty_name:string} \ + -nargs 0 \ + -iscachable_p "t" + + operation \ + -operation_name GetAssignees \ + -operation_desc "Get the assignees as a Tcl list of party_ids, of the default assignees for this case, object, role" \ + -inputspec {case_id:integer,object_id:integer,role_id:integer} \ + -outputspec {party_ids:[integer]} \ + -nargs 3 \ + -iscachable_p "f" +} + +} + +# +# Corresponding Service Contract Definition API +# + +ad_proc -public acs_sc::contract::new { + {-contract_name:required} + {-contract_dec {}} + {-oprations} +} { + insert -contract_name $contract_name -contract_desc $contract_desc + + if { [exists_and_not_null operations] } { + + namespace eval ::acs_sc::contract::define variable contract_name $contract_name + + namespace eval ::acs_sc::contract::define $operations + } +} + +ad_proc -public acs_sc::contract::define::operation { + {-operation_name:required} + {-operation_desc {}} + {-inputspec {}} + {-outputspec {}} + {-nargs 0} + {-iscachable_p "f"} +} { + variable contract_name + + set inputtype "${contract_name}.${operatoin_name}.InputType" + + acs_sc::msg_type::insert \ + -msg_type_name $inputtype \ + -msg_type_spec $inputspec + + set outputtype "${contract_name}.${operation_name}.OutputType" + + acs_sc::msg_type::insert \ + -msg_type_name $outputtype \ + -msg_type_spec $outputspec + + acs_sc::operation::insert \ + -contract_name $contract_name \ + -operation_desc $operation_desc \ + -inputtype $inputtype \ + -outputtype $outputtype \ + -nargs $nargs \ + -iscachable_p $iscachable_p +} + + + + +############################################################# + + +proc create_service_contracts {} { + acs_sc::contract::new DefaultAssignees { + operation GetObjectType + operation GetPrettyName + operation GetAssignees + } + acs_sc::contract::new SideEffect { + operation GetObjectType + operation GetPrettyName + operation DoSideEffect + } +} + +namespace eval acs_sc::contract {} +namespace eval acs_sc::contract::define {} + +proc acs_sc::contract::new { contract_name {operations {}} } { + puts "New Contract: $contract_name" + if { ![string equal $operations ""] } { + puts "Operations:" + namespace eval ::acs_sc::contract::define variable contract_name $contract_name + namespace eval ::acs_sc::contract::define $operations + } +} + +proc acs_sc::contract::define::operation { operation_name } { + variable contract_name + puts "Operation: ${contract_name}.${operation_name}" +} + +create_service_contracts + Index: openacs-4/packages/workflow/tcl/role-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/role-procs.tcl,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/role-procs.tcl 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/role-procs.tcl 14 Jan 2003 15:09:16 -0000 1.2 @@ -7,23 +7,41 @@ @cvs-id $Id$ } -namespace eval workflow::role { +namespace eval workflow::role {} - ad_proc -public add { - {-workflow_id:required} - {-short_name:required} - {-pretty_name:required} - } { - Creates a new role for a certain workflow. - - @param workflow_id - @param short_name - @param pretty_name - - @author Peter Marklund - } { - set role_id [db_nextval "wf_workflow_roles_seq"] +##### +# +# workflow::role namespace +# +##### - db_dml do_insert {} - } +ad_proc -public workflow::role::add { + {-workflow_id:required} + {-short_name:required} + {-pretty_name:required} +} { + Creates a new role for a certain workflow. + + @param workflow_id + @param short_name + @param pretty_name + + @author Peter Marklund +} { + set role_id [db_nextval "workflow_roles_seq"] + + db_dml do_insert {} } + +ad_proc -public workflow::role::get_id { + {-workflow_id:required} + {-short_name:required} +} { + Return the role_id of the role with the given short_name in the given workflow. + + @param workflow_id The ID of the workflow + @param short_name The short_name of the role + @return role_id of the desired role, or the empty string if it can't be found. +} { + return [db_string select_role_id {} -default {}] +} Index: openacs-4/packages/workflow/tcl/role-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/role-procs.xql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/role-procs.xql 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/role-procs.xql 14 Jan 2003 15:09:16 -0000 1.2 @@ -10,4 +10,13 @@ + + + select role_id + from workflow_roles + where workflow_id = :workflow_id + and short_name = :short_name + + + Index: openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/workflow-procs-postgresql.xql 14 Jan 2003 15:09:16 -0000 1.2 @@ -16,4 +16,10 @@ + + + select acs_object__delete(:workflow_id); + + + Index: openacs-4/packages/workflow/tcl/workflow-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/workflow-procs.tcl,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/workflow-procs.tcl 10 Jan 2003 13:38:37 -0000 1.1 +++ openacs-4/packages/workflow/tcl/workflow-procs.tcl 14 Jan 2003 15:09:16 -0000 1.2 @@ -7,9 +7,20 @@ @cvs-id $Id$ } -namespace eval ::workflow {} +namespace eval workflow {} +namespace eval workflow::service_contract {} -ad_proc -public ::workflow::add { +##### +# +# workflow namespace +# +##### + +ad_proc -public workflow::package_key {} { + return "workflow" +} + +ad_proc -public workflow::add { {-short_name:required} {-pretty_name:required} {-object_id:required} @@ -42,5 +53,92 @@ # that sets the scope of the workflow set context_id $object_id - db_dml do_insert {} + return [db_string do_insert {}] } + +ad_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 +} { + return [db_string do_delete {}] +} + +ad_proc -public workflow::get_id { + {-object_id:required} + {-short_name:required} +} { + Get workflow_id by short_name and object_id. + + @param object_id The ID of the object the workflow's for (typically a package instance) + @param short_name the short name of the workflow you want + + @author Lars Pind (lars@collaboraid.biz) +} { + return [db_string select_workflow_id {} -default {}] +} + +ad_proc -public workflow::action::get_initial_action { + {-workflow_id:required} +} { + Get the action_id of the special 'open' action of a workflow. + + @param workflow_id The ID of the workflow + @return action_id of the magic 'open' action + + @author Lars Pind (lars@collaboraid.biz) +} { + return [db_string select_initial_action {}] +} + +ad_proc -private workflow::default_sort_order { + {-workflow_id:required} + 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 + + @author Peter Marklund +} { + set sort_order_current \ + [db_string max_sort_order "select max(sort_order) \ + from $table_name \ + where workflow_id = $workflow_id" \ + -default 0] + + set sort_order [expr $sort_order_current + 1] + + return $sort_order +} + +##### +# +# workflow::service_contract +# +##### + +ad_proc -public workflow::service_contract::role_default_assignee {} { + return "Role_DefaultAssignees" +} + +ad_proc -public workflow::service_contract::role_assignee_pick_list {} { + return "Role_AssigneePickList" +} + +ad_proc -public workflow::service_contract::role_assignee_subquery {} { + return "Role_AssigneeSubQuery" +} + +ad_proc -public workflow::service_contract::action_side_effect {} { + return "Action_SideEffect" +} + +ad_proc -public workflow::service_contract::activity_log_format_title {} { + return "ActivityLog_FormatTitle" +} + Index: openacs-4/packages/workflow/tcl/workflow-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/workflow-procs.xql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/workflow-procs.xql 14 Jan 2003 15:09:16 -0000 1.1 @@ -0,0 +1,21 @@ + + + + + + select workflow_id + from workflows + where object_id = :object_id + and short_name = :short_name + + + + + + select action_id + from workflow_initial_action + where workflow_id = :workflow_id + + + + Index: openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl 14 Jan 2003 15:09:27 -0000 1.1 @@ -0,0 +1,165 @@ +ad_library { + Test cases for the Tcl API of the workflow package. The test cases are based + on the acs-automated-testing package. + + @author Peter Marklund + @creation-date 10 January 2003 + @cvs-id $Id: workflow-test-procs.tcl,v 1.1 2003/01/14 15:09:27 peterm Exp $ +} + +namespace eval workflow::test {} + +ad_proc workflow::test::bug_workflow_name {} { + The short name used for the Bug Tracker Bug test + workflow. It is assumed this short name will not be + present in the system. +} { + return "bug_test" +} + +ad_proc workflow::test::bug_workflow_object_id {} { + +} { + return [db_string main_site_package_id {select object_id + from site_nodes + where parent_id is null}] +} + +ad_proc workflow::test::workflow_setup {} { + Create a test workflow for the Bug Tracker + Bug use case. +} { + ##### + # + # Workflow + # + ##### + + set main_site_package_id [workflow::test::bug_workflow_object_id] + # Cannot use bt_bug as we cannot assume Bug Tracker to be installed + # TODO: test side_effects? + # -side_effects { bug-tracker.FormatLogTitle } + set workflow_id [workflow::add \ + -short_name [workflow::test::bug_workflow_name] \ + -pretty_name "Bug Test" \ + -object_id $main_site_package_id \ + -object_type "acs_object"] + + ##### + # + # Roles + # + ##### + + # TODO: add assignment rules? + # -assignment_rules { workflow.CreationUser } + workflow::role::add -workflow_id $workflow_id \ + -short_name "submitter" \ + -pretty_name "Submitter" + + # TODO: add assignment rules? + # -assignment_rules { + # bug-tracker.ComponentMaintainer + # bug-tracker.ProjectMaintainer + # } + workflow::role::add -workflow_id $workflow_id \ + -short_name "assignee" \ + -pretty_name "Assignee" + + ##### + # + # States + # + ##### + + workflow::fsm::state::add -workflow_id $workflow_id \ + -short_name "open" \ + -pretty_name "Open" + + workflow::fsm::state::add -workflow_id $workflow_id \ + -short_name "resolved" \ + -pretty_name "Resolved" + + workflow::fsm::state::add -workflow_id $workflow_id \ + -short_name "closed" \ + -pretty_name "Closed" + + ##### + # + # Actions + # + ##### + + workflow::fsm::action::add -workflow_id $workflow_id \ + -short_name "comment" \ + -pretty_name "Comment" \ + -pretty_past_tense "Commented" \ + -allowed_roles { submitter assignee } \ + -privileges { read } + + workflow::fsm::action::add -workflow_id $workflow_id \ + -short_name "edit" \ + -pretty_name "Edit" \ + -pretty_past_tense "Edited" \ + -allowed_roles { submitter assignee } \ + -privileges { write } + + # TODO add side effects? + # -side_effects { bug-tracker.CaptureResolutionCode } + workflow::fsm::action::add -workflow_id $workflow_id \ + -short_name "resolve" \ + -pretty_name "Resolve" \ + -pretty_past_tense "Resolved" \ + -assigned_role { assignee } \ + -enabled_states { open resolved } \ + -new_state "resolved" \ + -privileges { write } + + workflow::fsm::action::add -workflow_id $workflow_id \ + -short_name "close" \ + -pretty_name "Close" \ + -pretty_past_tense "Closed" \ + -assigned_role { submitter } \ + -enabled_states { resolved } \ + -new_state "closed" \ + -privileges { write } + + workflow::fsm::action::add -workflow_id $workflow_id \ + -short_name "reopen" \ + -pretty_name "Reopen" \ + -pretty_past_tense "Closed" \ + -allowed_roles { submitter } \ + -enabled_states { resolved closed } \ + -new_state "open" \ + -privileges { write } +} + +ad_proc workflow::test::workflow_teardown {} { + Delete the Bug Tracker Bug test workflow. +} { + set workflow_id [workflow::get_id -object_id [workflow::test::bug_workflow_object_id] \ + -short_name [workflow::test::bug_workflow_name]] + + workflow::delete -workflow_id $workflow_id +} + +aa_register_case bugtracker_workflow_create { + Test creation and teardown of an FSM workflow. + + @author Peter Marklund + @creation-date 10 January 2003 +} { + # Make sure to run the teardown proc even if there is an error + #set error_p [catch workflow::test::workflow_setup error] + workflow::test::workflow_setup + + # Any assertions here? + + workflow::test::workflow_teardown + + # Any assertions here? + + # Report any errors from the setup proc + #global errorInfo + #aa_false "error during setup: $error - $errorInfo" $error_p +}