Index: openacs-4/packages/workflow/sql/oracle/workflow-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-create.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-create.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,12 @@ +-- Data model for the workflow package, part of the OpenACS system. +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @author Peter Marklund (peter@collaboraid.biz) +-- @creation-date 8 Januar 2003 +-- +-- This is free software distributed under the terms of the GNU Public +-- License. Full text of the license is available from the GNU Project: +-- http://www.fsf.org/copyleft/gpl.html + +@@workflow-tables-create.sql +@@workflow-procedural-create.sql Index: openacs-4/packages/workflow/sql/oracle/workflow-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-drop.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-drop.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,12 @@ +-- Drop data model for the workflow package, part of the OpenACS system. +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @author Peter Marklund (peter@collaboraid.biz) +-- @creation-date 9 Januar 2003 +-- +-- This is free software distributed under the terms of the GNU Public +-- License. Full text of the license is available from the GNU Project: +-- http://www.fsf.org/copyleft/gpl.html + +@@workflow-tables-drop.sql +@@workflow-procedural-drop.sql Index: openacs-4/packages/workflow/sql/oracle/workflow-procedural-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-procedural-create.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-procedural-create.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,195 @@ +-- Procedural database code for the workflow package, a package in the OpenACS system. +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @author Peter Marklund (peter@collaboraid.biz) +-- +-- This is free software distributed under the terms of the GNU Public +-- License. Full text of the license is available from the GNU Project: +-- http://www.fsf.org/copyleft/gpl.html + +--------------------------------- +-- Workflow level, Generic Model +--------------------------------- + +create or replace package workflow +as + function delete( + delete_workflow_id in integer + ) return integer; + + function new( + short_name in varchar, + pretty_name in varchar, + package_key in varchar, + object_id in integer, + object_type in varchar, + creation_user in integer, + creation_ip in varchar, + context_id in integer + ) return integer; + +end workflow; +/ +show errors + +-- package bodies + +create or replace package body workflow +as + function delete( + delete_workflow_id in integer + ) return integer + is + begin + acs_object.delete(delete_workflow_id); + return 0; + end delete; + + + -- Function for creating a workflow + function new( + short_name in varchar, + pretty_name in varchar, + package_key in varchar, + object_id in integer, + object_type in varchar, + creation_user in integer, + creation_ip in varchar, + context_id in integer + ) return integer + is + v_workflow_id integer; + begin + -- Instantiate the ACS Object super type with auditing info + v_workflow_id := acs_object.new( + object_id => null, + object_type => 'workflow_lite', + creation_date => sysdate(), + creation_user => creation_user, + creation_ip => creation_ip, + context_id => context_id + ); + + -- Insert workflow specific info into the workflows table + insert into workflows + (workflow_id, short_name, pretty_name, package_key, object_id, object_type) + values + (v_workflow_id, short_name, pretty_name, package_key, object_id, object_type); + + return v_workflow_id; + end new; + +end workflow; +/ +show errors + +create or replace package workflow_case +as function get_pretty_state( + workflow_short_name in varchar, + object_id in integer + ) return varchar; + +end workflow_case; +/ +show errors + +-- Function for getting the pretty state of a case +create or replace package body workflow_case +as + function get_pretty_state( + workflow_short_name in varchar, + object_id in integer + ) return varchar + is + v_state_pretty varchar(4000); + v_object_id integer; + begin + v_object_id := object_id; + + select s.pretty_name + into v_state_pretty + from workflows w, + workflow_cases c, + workflow_case_fsm cfsm, + workflow_fsm_states s + where w.short_name = workflow_short_name + and c.object_id = v_object_id + and c.workflow_id = w.workflow_id + and cfsm.case_id = c.case_id + and s.state_id = cfsm.current_state; + + return v_state_pretty; + + end get_pretty_state; + +end workflow_case; +/ +show errors + +-- --create or replace package workflow_activity_log +-- --as +-- function new( +-- case_id in integer, +-- action_id in integer, +-- comment_format in varchar +-- ) return integer; + +-- end workflow_activity_log; +-- / +-- show errors + +-- create or replace package body workflow_activity_log +-- as +-- function new( +-- case_id in integer, +-- action_id in integer, +-- comment_format in varchar +-- ) return integer +-- is +-- v_item_id cr_items.item_id%TYPE; +-- v_revision_id cr_revisions.revision_id%TYPE; +-- begin +-- v_item_id := content_item.new ( +-- name, +-- parent_id, +-- item_id, +-- locale, +-- creation_date, +-- creation_user, +-- context_id, +-- creation_ip, +-- 'content_item', +-- content_type, +-- null, +-- null, +-- null, +-- null, +-- null +-- ); + +-- v_revision_id := content_revision.new ( +-- title, +-- description, +-- publish_date, +-- mime_type, +-- nls_language, +-- null, +-- v_item_id, +-- revision_id, +-- creation_date, +-- creation_user, +-- creation_ip +-- ); + +-- insert into workflow_case_log +-- (entry_id, case_id, action_id, comment_format) +-- values (v_revision_id, case_id, action_id, comment_format); + +-- content_item.set_live_revision (v_revision_id); + +-- end new; + +-- end workflow_activity_log; +-- / +--show errors + Index: openacs-4/packages/workflow/sql/oracle/workflow-procedural-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-procedural-drop.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-procedural-drop.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,17 @@ +-- Drop procedural database code for the workflow package, a package in the OpenACS system. +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @author Peter Marklund (peter@collaboraid.biz) +-- +-- This is free software distributed under the terms of the GNU Public +-- License. Full text of the license is available from the GNU Project: +-- http://www.fsf.org/copyleft/gpl.html + +--------------------------------- +-- Workflow level, Generic Model +--------------------------------- +drop package workflow; +drop package workflow_case; +drop package workflow_activity_log; + + Index: openacs-4/packages/workflow/sql/oracle/workflow-service-contracts-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-service-contracts-create.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-service-contracts-create.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,64 @@ +-- +-- Workflow Service Contracts +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @version $Id: workflow-service-contracts-create.sql,v 1.1 2003/02/12 14:21:56 peterm Exp $ +-- +-- GNU GPL v2 +-- + +-- +-- The service contract for workflows +-- + +begin + + acs_sc_contract.new ( + contract_name => 'NotificationType', + contract_desc => 'Notification Type' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.GetURL.InputType', + msg_type_spec => 'object_id:integer' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.GetURL.OutputType', + msg_type_spec => 'url:string' + ); + + acs_sc_operation.new ( + contract_name => 'NotificationType', + operation_name => 'GetURL', + operation_desc => 'gets the URL for an object in this notification type', + operaion_iscachable_p => 'f', + operation_nargs => 1, + operation_inputtype => 'NotificationType.GetURL.InputType', + operation_outputtype => 'NotificationType.GetURL.OutputType' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.ProcessReply.InputType', + msg_type_spec => 'reply_id:integer' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.ProcessReply.OutputType', + msg_type_spec => 'success_p:boolean' + ); + + acs_sc_operation.new + contract_name => 'NotificationType', + operation_name => 'ProcessReply', + operation_desc => 'Process a single reply', + operation_iscachable_p => 'f', + operation_nargs => 1, + operation_inputtype => 'NotificationType.ProcessReply.InputType', + operation_outputtype => 'NotificationType.ProcessReply.OutputType' + ); + +end; +/ +show errors + Index: openacs-4/packages/workflow/sql/oracle/workflow-service-contracts-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-service-contracts-drop.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-service-contracts-drop.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,64 @@ +-- +-- Workflow Service Contracts +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @version $Id: workflow-service-contracts-drop.sql,v 1.1 2003/02/12 14:21:56 peterm Exp $ +-- +-- GNU GPL v2 +-- + +-- +-- The service contract for workflows +-- + +begin + + acs_sc_contract.new ( + contract_name => 'NotificationType', + contract_desc => 'Notification Type' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.GetURL.InputType', + msg_type_spec => 'object_id:integer' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.GetURL.OutputType', + msg_type_spec => 'url:string' + ); + + acs_sc_operation.new ( + contract_name => 'NotificationType', + operation_name => 'GetURL', + operation_desc => 'gets the URL for an object in this notification type', + operaion_iscachable_p => 'f', + operation_nargs => 1, + operation_inputtype => 'NotificationType.GetURL.InputType', + operation_outputtype => 'NotificationType.GetURL.OutputType' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.ProcessReply.InputType', + msg_type_spec => 'reply_id:integer' + ); + + acs_sc_msg_type.new ( + msg_type_name => 'NotificationType.ProcessReply.OutputType', + msg_type_spec => 'success_p:boolean' + ); + + acs_sc_operation.new + contract_name => 'NotificationType', + operation_name => 'ProcessReply', + operation_desc => 'Process a single reply', + operation_iscachable_p => 'f', + operation_nargs => 1, + operation_inputtype => 'NotificationType.ProcessReply.InputType', + operation_outputtype => 'NotificationType.ProcessReply.OutputType' + ); + +end; +/ +show errors + Index: openacs-4/packages/workflow/sql/oracle/workflow-tables-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-tables-create.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-tables-create.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,444 @@ +-- Data model for the workflow package, part of the OpenACS system. +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @author Peter Marklund (peter@collaboraid.biz) +-- +-- @creation-date 9 January 2003 +-- +-- This is free software distributed under the terms of the GNU Public +-- License. Full text of the license is available from the GNU Project: +-- http://www.fsf.org/copyleft/gpl.html + +--------------------------------- +-- Workflow level, Generic Model +--------------------------------- + +-- Create the workflow object type +-- We use workflow_lite rather than just workflow +-- to avoid a clash with the old workflow package acs-workflow +begin + acs_object_type.create_type ( + object_type => 'workflow_lite', + pretty_name => 'Workflow Lite', + pretty_plural => 'Workflow Lites', + supertype => 'acs_object', + table_name => 'workflows', + id_column => 'workflow_id', + package_name => null, + abstract_p => 'f', + type_extension_table => null, + name_method => null + ); +end; +/ +show errors + +-- 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 constraint wfs_pk + primary key + constraint wfs_workflow_id_fk + references acs_objects(object_id) + on delete cascade, + short_name varchar(100) + constraint wfs_short_name_nn + not null, + pretty_name varchar(200) + constraint wfs_pretty_name_nn + not null, + object_id constraint wfs_object_id_fk + references acs_objects(object_id) + on delete cascade, + package_key varchar(100) + constraint wfs_package_key_nn + not null + constraint wfs_apm_package_types_fk + references apm_package_types(package_key), + -- object_id points to either a package type, package instance, or single workflow case + -- For Bug Tracker, every package instance will get its own workflow instance that is a copy + -- of the workflow instance for the Bug Tracker package type + object_type varchar(1000) + constraint wfs_object_type_nn + not null + constraint wfs_object_type_fk + references acs_object_types(object_type) + on delete cascade, + constraint wfs_oid_sn_un + unique (package_key, object_id, short_name) +); + +-- For callbacks on workflow +create table workflow_callbacks ( + workflow_id constraint wf_cbks_wid_nn + not null + constraint wf_cbks_wid_fk + references workflows(workflow_id) + on delete cascade, + acs_sc_impl_id constraint wf_cbks_sci_nn + not null + constraint wf_cbks_sci_fk + references acs_sc_impls(impl_id) + on delete cascade, + sort_order integer constraint wf_cbks_so_nn + not null, + constraint wf_cbks_pk + primary key (workflow_id, acs_sc_impl_id) +); + +create table workflow_roles ( + role_id integer constraint wf_roles_pk + primary key, + workflow_id constraint wf_roles_workflow_id_fk + references workflows(workflow_id) + on delete cascade, + short_name varchar(100) + constraint wf_roles_short_name_nn + not null, + pretty_name varchar(200) + constraint wf_roles_pretty_name_nn + not null, + sort_order integer constraint wf_roles_so_nn + not null +); + +create sequence workflow_roles_seq; + +-- Callbacks for roles +create table workflow_role_callbacks ( + role_id integer constraint wf_role_cbks_role_id_nn + not null + constraint wf_role_cbks_role_id_fk + references workflow_roles(role_id) + on delete cascade, + acs_sc_impl_id constraint wf_role_cbks_contract_id_nn + not null + constraint wf_role_cbks_contract_id_fk + references acs_sc_impls(impl_id) + on delete cascade, + -- this should be an implementation of any of the three assignment + -- service contracts: DefaultAssignee, AssigneePickList, or + -- AssigneeSubQuery + sort_order integer constraint wf_role_cbks_sort_order_nn + not null, + constraint wf_role_cbks_pk + primary key (role_id, acs_sc_impl_id), + constraint wf_role_asgn_rol_sort_un + unique (role_id, sort_order) +); + +create table workflow_actions ( + action_id integer + constraint wf_acns_pk + primary key, + workflow_id constraint wf_acns_workflow_id_nn + not null + constraint wf_acns_workflow_id_fk + references workflows(workflow_id) + on delete cascade, + sort_order integer constraint wf_acns_sort_order_nn + not null, + short_name varchar(100) + constraint wf_acns_short_name_nn + not null, + pretty_name varchar(200) + constraint wf_acns_pretty_name_nn + not null, + pretty_past_tense varchar(200), + edit_fields varchar(4000), + assigned_role constraint wf_acns_assigned_role_fk + references workflow_roles(role_id) + on delete set null, + always_enabled_p char(1) default 'f' +); + +create sequence workflow_actions_seq; + +-- Determines which roles are allowed to take certain actions +create table workflow_action_allowed_roles ( + action_id integer + constraint wf_acn_alwd_roles_acn_id_nn + not null + constraint wf_acn_alwd_roles_acn_id_fk + references workflow_actions(action_id) + on delete cascade, + role_id integer constraint wf_acn_alwd_roles_role_id_nn + not null + constraint wf_acn_alwd_roles_role_id_fk + references workflow_roles(role_id) + on delete cascade, + constraint wf_acn_alwd_roles_pk + primary key (action_id, role_id) +); + +-- Determines which privileges (on the object treated by a workflow case) will allow +-- users to take certain actions +create table workflow_action_privileges ( + action_id integer + constraint wf_acn_priv_acn_id_nn + not null + constraint wf_acn_priv_acn_id_fk + references workflow_actions(action_id) + on delete cascade, + privilege varchar(100) + constraint wf_acn_priv_privilege_nn + not null + constraint wf_acn_priv_privilege_fk + references acs_privileges(privilege) + on delete cascade, + constraint wf_acn_priv_pk + primary key (action_id, privilege) +); + +-- For callbacks on actions +create table workflow_action_callbacks ( + action_id integer + constraint wf_acn_cbks_acn_id_nn + not null + constraint wf_acn_cbks_acn_id_fk + references workflow_actions(action_id) + on delete cascade, + acs_sc_impl_id constraint wf_acn_cbks_sci_nn + not null + constraint wf_acn_cbks_sci_fk + references acs_sc_impls(impl_id) + on delete cascade, + sort_order integer constraint wf_acn_cbks_sort_order_nn + not null, + constraint wf_acn_cbks_pk + primary key (action_id, acs_sc_impl_id) +); + +-- For the initial action, which fires when a new case is started +create table workflow_initial_action ( + workflow_id constraint wf_initial_acn_pk + primary key + constraint wf_initial_acn_wf_fk + references workflows(workflow_id) + on delete cascade, + action_id constraint wf_initial_acn_act_fk + references workflow_actions(action_id) + on delete cascade +); + + +--------------------------------- +-- Workflow level, Finite State Machine Model +--------------------------------- + +create table workflow_fsm_states ( + state_id integer + constraint wf_fsm_states_pk + primary key, + workflow_id constraint wf_fsm_states_workflow_id_nn + not null + constraint wf_fsm_states_workflow_id_fk + references workflows(workflow_id) + on delete cascade, + sort_order integer constraint wf_fsm_states_sort_order_nn + not null, + -- The state with the lowest sort order is the initial state + short_name varchar(100) + constraint wf_fsm_states_short_name_nn + not null, + pretty_name varchar(200) + constraint wf_fsm_states_pretty_name_nn + not null, + hide_fields varchar(4000) +); + +create sequence workflow_fsm_states_seq; + +create table workflow_fsm_actions ( + action_id integer + constraint wf_fsm_acns_aid_fk + references workflow_actions(action_id) + on delete cascade + constraint wf_fsm_acns_pk + primary key, + new_state constraint wf_fsm_acns_new_st_fk + references workflow_fsm_states(state_id) + on delete cascade + -- can be null +); + +-- If an action is enabled in all states it won't have any entries in this table +create table workflow_fsm_action_en_in_st ( + action_id integer + constraint wf_fsm_acn_enb_in_st_acn_id_nn + not null + constraint wf_fsm_acn_enb_in_st_acn_id_fk + references workflow_fsm_actions(action_id) + on delete cascade, + state_id integer + constraint wf_fsm_acn_enb_in_st_st_id_nn + not null + constraint wf_fsm_acn_enb_in_st_st_id_fk + references workflow_fsm_states + on delete cascade +); + + + +-------------------------------------------------------- +-- Workflow level, context-dependent (assignments, etc.) +-------------------------------------------------------- + + +-- Static role-party map +create table workflow_role_default_parties ( + role_id integer constraint wf_role_default_parties_rid_nn + not null + constraint wf_role_default_parties_rid_fk + references workflow_roles(role_id) + on delete cascade, + party_id constraint wf_role_default_parties_pid_nn + not null + constraint wf_role_default_parties_pid_fk + references parties(party_id) + on delete cascade, + constraint wf_role_default_parties_pk + primary key (role_id, party_id) +); + +-- Static map between roles and parties allowed to be in those roles +create table workflow_role_allowed_parties ( + role_id integer constraint wf_role_alwd_parties_rid_nn + not null + constraint wf_role_alwd_parties_rid_fk + references workflow_roles(role_id) + on delete cascade, + party_id constraint wf_role_alwd_parties_pid_nn + not null + constraint wf_role_alwd_parties_pid_fk + references parties(party_id) + on delete cascade, + constraint wf_role_alwd_parties_pk + primary key (role_id, party_id) +); + + + + +--------------------------------- +-- Case level, Generic Model +--------------------------------- + +create sequence workflow_cases_seq; + +create table workflow_cases ( + case_id integer + constraint wf_cases_pk + primary key, + workflow_id constraint wf_cases_workflow_id_nn + not null + constraint wf_cases_workflow_id_fk + references workflows(workflow_id) + on delete cascade, + object_id constraint wf_cases_object_id_nn + not null + constraint wf_cases_object_id_fk + references acs_objects(object_id) + on delete cascade + constraint wf_cases_object_id_un + unique + -- the object which this case is about, e.g. the acs-object for a bug-tracker bug +); + +create table workflow_case_role_party_map ( + case_id integer + constraint wf_cs_role_pty_map_case_id_nn + not null + constraint wf_cs_role_pty_map_case_id_fk + references workflow_cases(case_id) + on delete cascade, + role_id integer constraint wf_cs_role_pty_map_role_id_nn + not null + constraint wf_cs_role_pty_map_role_id_fk + references workflow_roles(role_id) + on delete cascade, + party_id constraint wf_cs_role_pty_map_pty_id_nn + not null + constraint wf_cs_role_pty_map_pty_id_fk + references parties(party_id) + on delete cascade, + constraint wf_case_role_pty_map_pk + primary key (case_id, role_id, party_id) +); + +--------------------------------- +-- Case level, Finite State Machine Model +--------------------------------- + +create table workflow_case_fsm ( + case_id integer + constraint wf_case_fsm_case_id_nn + not null + constraint wf_case_fsm_case_id_fk + references workflow_cases(case_id) + on delete cascade, + current_state constraint wf_case_fsm_st_id_fk + references workflow_fsm_states(state_id) + on delete cascade +); + + +--------------------------------- +-- Case level, Activity Log +--------------------------------- + +--begin; +-- select content_type__create_type ( +-- 'workflow_activity_log', -- content_type +-- 'content_revision', -- supertype +-- 'Workflow Activity Log', -- pretty_name +-- 'Workflow Activity Log', -- pretty_plural +-- 'workflow_case_log', -- table_name +-- 'entry_id' -- id_column +-- ); +--end; + + +create sequence workflow_case_log_seq; + +create table workflow_case_log ( + entry_id integer + constraint wf_case_log_pk + primary key, + case_id integer + constraint wf_case_log_case_id_fk + references workflow_cases(case_id) + on delete cascade, + action_id integer + constraint wf_case_log_acn_id_fk + references workflow_actions(action_id) + on delete cascade, + user_id constraint workflow_case_log_user_id_fk + references users(user_id) + on delete cascade, + action_date date default sysdate + constraint workflow_case_log_acn_date_nn + not null, + comment_text varchar(4000), + comment_format varchar(50) + default 'text/plain' + constraint wf_clog_comment_format_nn + not null +); + +create table workflow_case_log_data ( + entry_id integer + constraint wf_case_log_data_eid_nn + not null + constraint wf_case_log_data_eid_fk + references workflow_case_log(entry_id) + on delete cascade, + key varchar(50), + value varchar(4000), + constraint wf_case_log_data_pk + primary key (entry_id, key) +); + Index: openacs-4/packages/workflow/sql/oracle/workflow-tables-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/sql/oracle/workflow-tables-drop.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/sql/oracle/workflow-tables-drop.sql 12 Feb 2003 14:21:56 -0000 1.1 @@ -0,0 +1,52 @@ +-- Drop table data for the workflow package, part of the OpenACS system. +-- +-- @author Lars Pind (lars@collaboraid.biz) +-- @author Peter Marklund (peter@collaboraid.biz) +-- @creation-date 9 Januar 2003 +-- +-- This is free software distributed under the terms of the GNU Public +-- License. Full text of the license is available from the GNU Project: +-- http://www.fsf.org/copyleft/gpl.html + +-- Drop all data in workflow tables by dropping the acs objects of all workflows in the system. +-- This is sufficient since all workflow data ultimately +-- hangs on workflow instances and will be dropped on cascade +begin + for row in (select object_id from acs_objects + where object_type = 'workflow_lite') + loop + acs_object.delete(row.object_id); + end loop; + + acs_object_type.drop_type('workflow_lite', 't'); +end; +/ +show errors + +-- Drop all tables +drop table workflow_case_fsm; +drop table workflow_case_role_party_map; +drop table workflow_case_log_data; +drop table workflow_case_log; +drop table workflow_cases; +drop table workflow_fsm_action_en_in_st; +drop table workflow_fsm_actions; +drop table workflow_initial_action; +drop table workflow_fsm_states; +drop table workflow_action_callbacks; +drop table workflow_action_privileges; +drop table workflow_action_allowed_roles; +drop table workflow_actions; +drop table workflow_role_callbacks; +drop table workflow_role_allowed_parties; +drop table workflow_role_default_parties; +drop table workflow_roles; +drop table workflow_callbacks; +drop table workflows; + +-- 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/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.4 -r1.5 --- openacs-4/packages/workflow/sql/postgresql/workflow-procedural-create.sql 3 Feb 2003 12:22:47 -0000 1.4 +++ openacs-4/packages/workflow/sql/postgresql/workflow-procedural-create.sql 12 Feb 2003 14:22:35 -0000 1.5 @@ -11,7 +11,7 @@ -- Workflow level, Generic Model --------------------------------- -create function workflow__delete (integer) +create or replace function workflow__delete (integer) returns integer as ' declare delete_workflow_id alias for $1; @@ -97,57 +97,86 @@ end; ' language 'plpgsql'; -create function workflow_activity_log__new (integer, --case_id - integer, --action_id - varchar -- comment_format - ) returns integer as ' +select define_function_args ('workflow_case_log_entry__new','item_id,content_type;workflow_case_log_entry,case_id,action_id,comment,comment_mime_type,creation_user,creation_ip'); + +create or replace function workflow_case_log_entry__new ( + integer, -- item_id + varchar, -- content_type + integer, -- case_id + integer, -- action_id + varchar, -- comment + varchar, -- comment_mime_type + integer, -- creation_user + varchar -- creation_ip +) returns integer as ' declare - new__case_id alias for $1; - new__action_id alias for $2; - new__comment_format alias for $3; - + p_item_id alias for $1; + p_content_type alias for $2; + p_case_id alias for $3; + p_action_id alias for $4; + p_comment alias for $5; + p_comment_mime_type alias for $6; + p_creation_user alias for $7; + p_creation_ip alias for $8; - v_item_id cr_items.item_id%TYPE; - v_revision_id cr_revisions.revision_id%TYPE; + v_name varchar; + v_action_short_name varchar; + v_action_pretty_past_tense varchar; + v_case_object_id integer; + v_item_id integer; + v_revision_id integer; begin + select short_name, pretty_past_tense + into v_action_short_name, v_action_pretty_past_tense + from workflow_actions + where action_id = p_action_id; - v_item_id := content_item__new ( - new__name, - new__parent_id, - new__item_id, - new__locale, - new__creation_date, - new__creation_user, - new__context_id, - new__creation_ip, - ''content_item'', - new__content_type, - null, - null, - null, - null, - null - ); + -- use case object as context_id + select object_id + into v_case_object_id + from workflow_cases + where case_id = p_case_id; - v_revision_id := content_revision__new ( - new__title, - new__description, - new__publish_date, - new__mime_type, - new__nls_language, - null, - v_item_id, - new__revision_id, - new__creation_date, - new__creation_user, - new__creation_ip + -- build the unique name + if p_item_id is not null then + v_item_id := p_item_id; + else + select nextval + into v_item_id + from acs_object_id_seq; + end if; + v_name := v_action_short_name || '' '' || v_item_id; + v_item_id := content_item__new ( + v_item_id, -- item_id + v_name, -- name + 0, -- parent_id + v_action_pretty_past_tense, -- title + now(), -- creation_date + p_creation_user, -- creation_user + v_case_object_id, -- context_id + p_creation_ip, -- creation_ip + ''t'', -- is_live + p_comment_mime_type, -- mime_type + p_comment, -- text + ''text'', -- storage_type + ''t'', -- security_inherit_p + ''CR_FILES'', -- storage_area_key + ''content_item'', -- item_subtype + p_content_type -- content_type ); - insert into workflow_case_log - (entry_id, case_id, action_id, comment_format) - values (v_revision_id, new__case_id, new__action_id, new__comment_format); + -- insert the row into the single-column entry revision table + select content_item__get_live_revision (v_item_id) + into v_revision_id; - PERFORM content_item__set_live_revision (v_revision_id); + insert into workflow_case_log_rev (entry_rev_id) + values (v_revision_id); -end; ' language 'plpgsql'; + -- insert into workflow-case-log + insert into workflow_case_log (entry_id, case_id, action_id) + values (v_item_id, p_case_id, p_action_id); + + -- return id of newly created item + return v_item_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.3 -r1.4 --- openacs-4/packages/workflow/sql/postgresql/workflow-procedural-drop.sql 3 Feb 2003 12:22:47 -0000 1.3 +++ openacs-4/packages/workflow/sql/postgresql/workflow-procedural-drop.sql 12 Feb 2003 14:22:35 -0000 1.4 @@ -13,12 +13,28 @@ -- Drop all functions drop function workflow__delete (integer); -drop function workflow__new (varchar, -- short_name - varchar, -- pretty_name - varchar, -- package_key - integer, -- object_id - varchar, -- object_type - integer, -- creation_user - varchar, -- creation_ip - integer -- context_id - ); + +drop function workflow__new ( + varchar, -- short_name + varchar, -- pretty_name + varchar, -- package_key + integer, -- object_id + varchar, -- object_type + integer, -- creation_user + varchar, -- creation_ip + integer -- context_id +); + +drop function workflow_case_log_entry__new ( + integer, -- item_id + varchar, -- content_type + integer, -- case_id + integer, -- action_id + varchar, -- content + varchar, -- mime_type + integer, -- creation_user + varchar -- creation_ip +); + +delete from acs_function_args +where function = 'workflow_case_log_entry__new'; 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.7 -r1.8 --- openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql 4 Feb 2003 09:44:48 -0000 1.7 +++ openacs-4/packages/workflow/sql/postgresql/workflow-tables-create.sql 12 Feb 2003 14:22:35 -0000 1.8 @@ -425,45 +425,20 @@ -- Case level, Activity Log --------------------------------- ---begin; --- select content_type__create_type ( --- 'workflow_activity_log', -- content_type --- 'content_revision', -- supertype --- 'Workflow Activity Log', -- pretty_name --- 'Workflow Activity Log', -- pretty_plural --- 'workflow_case_log', -- table_name --- 'entry_id' -- id_column --- ); ---end; - - -create sequence workflow_case_log_seq; - create table workflow_case_log ( entry_id integer constraint wf_case_log_pk - primary key, + primary key + constraint wf_case_log_cr_items_fk + references cr_items(item_id), case_id integer constraint wf_case_log_case_id_fk references workflow_cases(case_id) on delete cascade, action_id integer constraint wf_case_log_acn_id_fk references workflow_actions(action_id) - on delete cascade, - user_id integer - constraint workflow_case_log_user_id_fk - references users(user_id) - on delete cascade, - action_date timestamp - constraint workflow_case_log_acn_date_nn - not null - default now(), - comment text, - comment_format varchar(50) - default 'text/plain' - constraint wf_clog_comment_format_nn - not null + on delete cascade ); create table workflow_case_log_data ( @@ -479,3 +454,12 @@ primary key (entry_id, key) ); +select content_type__create_type ( + 'workflow_case_log_entry', -- content_type + 'content_revision', -- supertype + 'Workflow Case Log Entry', -- pretty_name + 'Workflow Case Log Entries', -- pretty_plural + 'workflow_case_log_rev', -- table_name + 'entry_rev_id', -- id_column + null -- name_method +); 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.4 -r1.5 --- openacs-4/packages/workflow/sql/postgresql/workflow-tables-drop.sql 3 Feb 2003 12:22:47 -0000 1.4 +++ openacs-4/packages/workflow/sql/postgresql/workflow-tables-drop.sql 12 Feb 2003 14:22:35 -0000 1.5 @@ -43,6 +43,13 @@ drop table workflow_case_role_party_map; drop table workflow_case_log_data; drop table workflow_case_log; + +select content_type__drop_type ( + 'workflow_case_log_entry', -- content_type + 't', -- drop_children_p + 't' -- drop_table_p +); + drop table workflow_cases; drop table workflow_fsm_action_en_in_st; drop table workflow_fsm_actions; @@ -64,4 +71,5 @@ 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.5 -r1.6 --- openacs-4/packages/workflow/tcl/action-procs.tcl 3 Feb 2003 12:23:01 -0000 1.5 +++ openacs-4/packages/workflow/tcl/action-procs.tcl 12 Feb 2003 14:23:14 -0000 1.6 @@ -134,35 +134,27 @@ @param action_id The action_id of the action. @return role_id of the assigned role. } { - return [db_string select_assigned_role {}] + return [get_from_request_cache $action_id "assigned_role"] } ad_proc -public workflow::action::get_allowed_roles { {-action_id:required} } { - Return the assigned role of the given action + Return the allowed roles 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 {}] + return [get_from_request_cache $action_id "allowed_roles"] } ad_proc -public workflow::action::get_privileges { {-action_id:required} - {-no_admin:boolean} } { 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 } { - set privileges [db_list select_privileges {}] - - # Admins always have privilege - if { !$no_admin_p } { - lappend privileges "admin" - } - - return $privileges + return [get_from_request_cache $action_id "privileges"] } ad_proc -public workflow::action::get_id { @@ -175,9 +167,42 @@ @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::refresh_request_cache $workflow_id + global __workflow_action_data,${workflow_id} + + foreach action_id [set __workflow_action_data,${workflow_id}(action_ids)] { + array set one_action [set __workflow_action_data,${workflow_id}($action_id)] + + if { [string equal $one_action(short_name) $short_name] } { + return $action_id + } + } + + error "workflow::action::get_id role with short_name $short_name not found for workflow $workflow_id" } +ad_proc -public workflow::action::get_workflow_id { + {-action_id:required} +} { + Lookup the workflow_id of a certain action_id. + + @author Peter Marklund +} { + return [util_memoize \ + [list workflow::action::get_workflow_id_not_cached -action_id $action_id]] +} + +ad_proc -private workflow::action::get_workflow_id_not_cached { + {-action_id:required} +} { + This is a proc that should only be used internally by the workflow + API, applications should use workflow::action::get_workflow_id instead. + + @author Peter Marklund +} { + return [db_string select_workflow_id {}] +} + ad_proc -public workflow::action::get { {-action_id:required} {-array:required} @@ -194,12 +219,7 @@ # Select the info into the upvar'ed Tcl Array upvar $array row - db_1row action_info {} -column_array row - - set row(callbacks) [db_list action_callbacks {}] - set row(allowed_roles) [db_list action_allowed_roles {}] - set row(privileges) [get_privileges -action_id $action_id -no_admin] - + array set row [get_from_request_cache $action_id] } ad_proc -public workflow::action::get_element { @@ -246,13 +266,43 @@ # Insert the callback db_dml insert_callback {} } + + # Action related data changed - need to flush + set workflow_id [workflow::action::get_workflow_id -action_id $action_id] + workflow::action::flush_cache -workflow_id $workflow_id + return $acs_sc_impl_id } +ad_proc -private workflow::action::get_callbacks { + {-action_id:required} + {-contract_name:required} +} { + Return a list of implementation names for the callbacks of a given workflow action. + @see workflow::case::role::get_callbacks + @author Peter Marklund +} { + array set callbacks [get_from_request_cache $action_id callbacks_array] + set callback_ids [get_from_request_cache $action_id callback_ids] + + # Loop over the callbacks and return the impl_names of those with a matching + # contract name + set impl_names [list] + foreach callback_id $callback_ids { + array set one_callback $callbacks($callback_id) + if { [string equal $one_callback(contract_name) $contract_name] } { + lappend impl_names $one_callback(impl_name) + } + } + return $impl_names +} + + + ##### # # workflow::action::fsm @@ -321,16 +371,19 @@ db_dml insert_enabled_state {} } } + + # Action info for this workflow changed, need to flush + workflow::action::flush_cache -workflow_id $workflow_id } ad_proc -public workflow::action::fsm::get_new_state { {-action_id:required} } { - Return the new state for an action + Return the ID of 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 The ID of 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 {}] + return [workflow::action::get_from_request_cache $action_id "new_state_id"] } ad_proc -public workflow::action::fsm::get { @@ -347,14 +400,6 @@ upvar $array row workflow::action::get -action_id $action_id -array row - - # Get new_state - db_0or1row action_fsm_info {} -column_array additional_row - array set row [array get additional_row] - - # Get enabled_states - set row(enabled_states) [db_list action_enabled_short_name {}] - } ad_proc -public workflow::action::fsm::get_element { @@ -465,6 +510,10 @@ array unset row workflow_id array unset row sort_order array unset row assigned_role_short_name + array unset row new_state_id + array unset row callbacks_array + array unset row callback_ids + array unset row allowed_roles_array # Get rid of a few defaults array set defaults { initial_action_p f always_enabled_p f } @@ -489,7 +538,79 @@ return $spec } + +ad_proc -private workflow::action::flush_cache { + {-workflow_id:required} +} { + Flush all caches related to actions for the given + workflow_id. Used internally by the workflow API only. + + @author Peter Marklund +} { + # Flush the request cache + global __workflow_action_data,${workflow_id} + if { [info exists __workflow_action_data,${workflow_id}] } { + foreach action_id [set __workflow_action_data,${workflow_id}(action_ids)] { + global __workflow_one_action,$action_id + + if { [info exists __workflow_one_action,$action_id] } { + unset __workflow_one_action,$action_id + } + } + + unset __workflow_action_data,${workflow_id} + } + + # Flush the thread global cache + util_memoize_flush [list workflow::action::get_all_info_not_cached -workflow_id $workflow_id] +} + +ad_proc -private workflow::action::refresh_request_cache { workflow_id } { + Initializes the cached array with information about actions for a certain workflow + so that it can be reused within one request. + + @author Peter Marklund +} { + global __workflow_action_data,${workflow_id} + + if { ![info exists __workflow_action_data,${workflow_id}] } { + array set __workflow_action_data,${workflow_id} [workflow::action::get_all_info -workflow_id $workflow_id] + } +} +ad_proc -private workflow::action::get_from_request_cache { + action_id + {element ""} +} { + This provides some abstraction for the Workflow API cache + and also some optimization - we only convert lists to + arrays once per request. Should be used internally + by the workflow API only. + + @author Peter Marklund +} { + # Get the cache with all actions + set workflow_id [workflow::action::get_workflow_id -action_id $action_id] + refresh_request_cache $workflow_id + global __workflow_action_data,${workflow_id} + + array set workflow_data [workflow::action::get_all_info -workflow_id $workflow_id] + + # A single action + set action_var_name __workflow_one_action,${action_id} + global $action_var_name + + if { ![info exists $action_var_name] } { + array set $action_var_name [set __workflow_action_data,${workflow_id}($action_id)] + } + + if { [empty_string_p $element] } { + return [array get $action_var_name] + } else { + return [set "${action_var_name}($element)"] + } +} + ad_proc -private workflow::action::fsm::generate_actions_spec { {-workflow_id:required} } { @@ -502,11 +623,138 @@ @author Lars Pind (lars@collaboraid.biz) } { # actions(short_name) { ... action-spec ... } - set actions [list] + array set actions [list] foreach action_id [workflow::get_actions -workflow_id $workflow_id] { - lappend actions [get_element -action_id $action_id -element short_name] [generate_spec -action_id $action_id] + lappend actions_list [get_element -action_id $action_id -element short_name] [generate_spec -action_id $action_id] } + + return $actions_list + +} + +ad_proc -private workflow::action::get_all_info { + {-workflow_id:required} +} { + This proc is for internal use in the workflow API only. + Returns all information related to actions for a certain + workflow instance. Uses util_memoize to cache values. + + @see workflow::action::get_all_info_not_cached + + @author Peter Marklund +} { + # LARS HACK +# return [workflow::action::get_all_info_not_cached \ + # -workflow_id $workflow_id] + + return [util_memoize [list workflow::action::get_all_info_not_cached \ + -workflow_id $workflow_id] [workflow::cache_timeout]] +} + +ad_proc -private workflow::action::get_all_info_not_cached { + {-workflow_id:required} +} { + This proc is for internal use in the workflow API only and + should not be invoked directly from application code. Returns + all information related to actions for a certain workflow instance. + Goes to the database on every invocation and should be used together + with util_memoize. + + @author Peter Marklund +} { + # We avoid nested db queries in this proc to enhance performance + + # Put scalar action data into the master array and use + # a list of action_id:s for sorting purposes + array set action_data {} + set action_ids [list] + db_foreach action_info {} -column_array action_row { + + # Cache the mapping action_id -> workflow_id + util_memoize_seed \ + [list workflow::action::get_workflow_id_not_cached -action_id $action_row(action_id)] \ + $workflow_id + + set action_data($action_row(action_id)) [array get action_row] + lappend action_ids $action_row(action_id) + } - return $actions + # Build a separate array for all action callbacks of the workflow + array set callbacks_array {} + array set callbacks {} + array set callback_ids_array {} + set callback_ids [list] + set last_action_id "" + db_foreach action_callbacks {} -column_array callback_row { + set callbacks_array($callback_row(action_id),$callback_row(impl_id)) [array get callback_row] + lappend callbacks($callback_row(action_id)) \ + "$callback_row(impl_owner_name).$callback_row(impl_name)" + + if { ![string equal $last_action_id $callback_row(action_id)] } { + set callback_ids_array($last_action_id) $callback_ids + set callback_ids [list] + } + lappend callback_ids $callback_row(impl_id) + set last_action_id $callback_row(action_id) + } + # Peter had forgotten this at the end of the loop + set callback_ids_array($last_action_id) $callback_ids + + # Build an array for all allowed roles for all actions + array set allowed_roles_array {} + array set allowed_roles {} + db_foreach action_allowed_roles {} -column_array allowed_role_row { + set allowed_roles_array($allowed_role_row(action_id),$allowed_role_row(role_id)) [array get allowed_role_row] + lappend allowed_roles($allowed_role_row(action_id)) $allowed_role_row(short_name) + } + + # Build an array of privileges for all actions + array set privileges {} + db_foreach select_privileges {} { + lappend privileges($action_id) $privilege + } + + # Build an erray of enabled state short names for all actions + array set enabled_states {} + db_foreach action_enabled_short_name {} { + lappend enabled_states($action_id) $short_name + } + + # For each action_id, add to the array of that action the contents of the + # sub arrays (callbacks, allowed_roles, privileges) + foreach action_id $action_ids { + array set one_action $action_data($action_id) + + foreach array_name { privileges enabled_states callbacks allowed_roles } { + if { [info exists ${array_name}($action_id)] } { + set one_action(${array_name}) [set ${array_name}($action_id)] + } else { + set one_action(${array_name}) {} + } + } + + set id_len [expr [string length $action_id] + 1] + foreach array_name { callbacks_array allowed_roles_array } { + set one_action($array_name) [list] + foreach { key value } [array get $array_name "${action_id},*"] { + lappend one_action($array_name) [string range $key $id_len end] $value + } + } + + if { [info exists callback_ids_array($action_id)] } { + set one_action(callback_ids) $callback_ids_array($action_id) + } else { + set one_action(callback_ids) [list] + } + + set action_data($action_id) [array get one_action] + + # Have to unset the array as otherwise array set will append to previous values + unset one_action + } + + set action_data(action_ids) $action_ids + + return [array get action_data] } 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.5 -r1.6 --- openacs-4/packages/workflow/tcl/action-procs.xql 3 Feb 2003 12:23:01 -0000 1.5 +++ openacs-4/packages/workflow/tcl/action-procs.xql 12 Feb 2003 14:23:14 -0000 1.6 @@ -56,41 +56,19 @@ - + - select assigned_role - from workflow_actions - where action_id = :action_id + select p.privilege, + p.action_id + from workflow_action_privileges p, + workflow_actions a + where a.action_id = p.action_id + and a.workflow_id = :workflow_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 - - - - - select a.action_id, a.workflow_id, a.sort_order, @@ -105,33 +83,71 @@ from workflow_initial_action where workflow_id = a.workflow_id and action_id = a.action_id - ) as initial_action_p - from workflow_actions a - where a.action_id = :action_id + ) as initial_action_p, + fa.new_state as new_state_id, + (select short_name from workflow_fsm_states where state_id = fa.new_state) as new_state + from workflow_actions a left outer join + workflow_fsm_actions fa on (a.action_id = fa.action_id) + where a.workflow_id = :workflow_id + and fa.action_id = a.action_id + order by a.sort_order - + - select impl.impl_owner_name || '.' || impl.impl_name - from acs_sc_impls impl, - workflow_action_callbacks c - where c.action_id = :action_id - and impl.impl_id = c.acs_sc_impl_id - order by c.sort_order + select workflow_id + from workflow_actions + where action_id = :action_id - + - select r.short_name + select impl.impl_id, + impl.impl_name, + impl.impl_owner_name, + ctr.contract_name, + a.action_id + from workflow_action_callbacks ac, + workflow_actions a, + acs_sc_impls impl, + acs_sc_bindings bind, + acs_sc_contracts ctr + where ac.action_id = a.action_id + and a.workflow_id = :workflow_id + and impl.impl_id = ac.acs_sc_impl_id + and impl.impl_id = bind.impl_id + and bind.contract_id = ctr.contract_id + order by a.action_id, ac.sort_order + + + + + + select r.short_name, + r.role_id, + aar.action_id from workflow_roles r, workflow_action_allowed_roles aar - where aar.action_id = :action_id + where r.workflow_id = :workflow_id and r.role_id = aar.role_id + + + select s.short_name, + waeis.action_id + from workflow_fsm_action_en_in_st waeis, + workflow_actions a, + workflow_fsm_states s + where waeis.action_id = a.action_id + and a.workflow_id = :workflow_id + and s.state_id = waeis.state_id + + + select coalesce(max(sort_order),0) + 1 @@ -147,8 +163,6 @@ - - insert into workflow_fsm_actions @@ -166,33 +180,4 @@ - - - select new_state - from workflow_fsm_actions a - where action_id = :action_id - - - - - - select a.new_state as new_state_id, - s.short_name as new_state - from workflow_fsm_actions a, - workflow_fsm_states s - where a.action_id = :action_id - and s.state_id = a.new_state - - - - - - select s.short_name - from workflow_fsm_action_en_in_st waeis, - workflow_fsm_states s - where waeis.action_id = :action_id - and s.state_id = waeis.state_id - - - Index: openacs-4/packages/workflow/tcl/case-procs-oracle.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/case-procs-oracle.xql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/case-procs-oracle.xql 12 Feb 2003 14:23:14 -0000 1.1 @@ -0,0 +1,28 @@ + + + oracle8.1.7 + + + + select state_id + from workflow_fsm_states + where workflow_id = :workflow_id + order by sort_order + limit 1 + + + + + + select m.party_id, + p.email, + acs_object.name(m.party_id) as name + from workflow_case_role_party_map m, + parties p + where m.case_id = :case_id + and m.role_id = :role_id + and p.party_id = m.party_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 -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/case-procs-postgresql.xql 14 Jan 2003 15:09:16 -0000 1.1 +++ openacs-4/packages/workflow/tcl/case-procs-postgresql.xql 12 Feb 2003 14:23:14 -0000 1.2 @@ -12,4 +12,44 @@ + + + select m.party_id, + p.email, + acs_object__name(m.party_id) as name + from workflow_case_role_party_map m, + parties p + where m.case_id = :case_id + and m.role_id = :role_id + and p.party_id = m.party_id + + + + + + select l.entry_id, + l.case_id, + l.action_id, + a.short_name as action_short_name, + a.pretty_name as action_pretty_name, + a.pretty_past_tense as action_pretty_past_tense, + io.creation_user, + iou.first_names as user_first_names, + iou.last_name as user_last_name, + iou.email as user_email, + io.creation_date, + to_char(io.creation_date, 'fmMM/DDfm/YYYY') as creation_date_pretty, + r.content as comment, + r.mime_type as comment_mime_type + from workflow_case_log l join + workflow_actions a using (action_id) join + cr_items i on (i.item_id = l.entry_id) join + acs_objects io on (io.object_id = i.item_id) join + cc_users iou on (iou.user_id = io.creation_user) join + cr_revisions r on (r.revision_id = i.live_revision) + where l.case_id = :case_id + order by creation_date + + + 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 -r1.4 -r1.5 --- openacs-4/packages/workflow/tcl/case-procs.tcl 3 Feb 2003 12:23:01 -0000 1.4 +++ openacs-4/packages/workflow/tcl/case-procs.tcl 12 Feb 2003 14:23:14 -0000 1.5 @@ -51,14 +51,14 @@ {-workflow_id:required} {-object_id:required} {-comment:required} - {-comment_format:required} + {-comment_mime_type:required} {-user_id} } { Start a new case for this workflow and object. @param object_id The object_id which the case is about @param workflow_short_name The short_name of the workflow. - @param comment_format html, plain or pre + @param comment_mime_type html, plain or pre @return The case_id of the case. Returns the empty string if no case could be found. @author Lars Pind (lars@collaboraid.biz) @@ -69,24 +69,17 @@ db_transaction { - ns_log Notice "LARS - case::new1: object_id = $object_id, [db_string foobar { select count(*) from acs_objects where object_id = :object_id }]" - # Insert the case set case_id [insert -workflow_id $workflow_id -object_id $object_id] - ns_log Notice "LARS - case::new2: object_id = $object_id, [db_string foobar { select count(*) from acs_objects where object_id = :object_id }]" - # Execute the initial action workflow::case::action::execute \ -case_id $case_id \ - -action_id [workflow::get_initial_action -workflow_id $workflow_id] \ + -action_id [workflow::get_element -workflow_id $workflow_id -element initial_action_id] \ -comment $comment \ - -comment_format $comment_format \ + -comment_mime_type $comment_mime_type \ -user_id $user_id \ - -no_check - - ns_log Notice "LARS - case::new3: object_id = $object_id, [db_string foobar { select count(*) from acs_objects where object_id = :object_id }]" - + -initial } return $case_id @@ -214,6 +207,7 @@ ad_proc -private workflow::case::assign_roles { {-case_id:required} + {-all:boolean} } { Find out which roles are assigned to currently enabled actions. If any of these currently have zero assignees, run the default @@ -225,10 +219,15 @@ } { 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 { ![empty_string_p $role_id] && [lsearch $role_id_list $role_id] == -1 } { - lappend role_id_list $role_id + if { $all_p } { + set workflow_id [workflow::case::get_element -case_id $case_id -element workflow_id] + set role_id_list [workflow::get_roles -workflow_id $workflow_id] + } else { + foreach action_id [get_enabled_actions -case_id $case_id] { + set role_id [workflow::action::get_assigned_role -action_id $action_id] + if { ![empty_string_p $role_id] && [lsearch $role_id_list $role_id] == -1 } { + lappend role_id_list $role_id + } } } @@ -247,6 +246,7 @@ + ##### # # workflow::case::role namespace @@ -265,15 +265,15 @@ @author Lars Pind (lars@collaboraid.biz) } { set contract_name [workflow::service_contract::role_default_assignees] - + db_transaction { + set impl_names [workflow::role::get_callbacks \ + -role_id $role_id \ + -contract_name $contract_name] + set object_id [workflow::case::get_element -case_id $case_id -element object_id] - - ns_log Notice "LARS - case::role::set_default_assignees: object_id = $object_id, [db_string foobar { select count(*) from acs_objects where object_id = :object_id }]" - - set impl_names [db_list select_callbacks {}] - + foreach impl_name $impl_names { # Call the service contract implementation set party_id_list [acs_sc::invoke \ @@ -283,20 +283,228 @@ -call_args [list $case_id $object_id $role_id]] 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 - } + assignee_insert -case_id $case_id -role_id $role_id -party_ids $party_id_list + # We stop when the first callback returned something break } } } } +ad_proc -public workflow::case::role::get_picklist { + {-case_id:required} + {-role_id:required} +} { + Get the picklist for this role. + + @param case_id the ID of the case. + @param role_id the ID of the role. + + @author Lars Pind (lars@collaboraid.biz) +} { + set contract_name [workflow::service_contract::role_assignee_pick_list] + + set party_id_list [list] + + db_transaction { + + set impl_names [workflow::role::get_callbacks \ + -role_id $role_id \ + -contract_name $contract_name] + + set object_id [workflow::case::get_element -case_id $case_id -element object_id] + + foreach impl_name $impl_names { + # Call the service contract implementation + set party_id_list [acs_sc::invoke \ + -contract $contract_name \ + -operation "GetPickList" \ + -impl $impl_name \ + -call_args [list $case_id $object_id $role_id]] + + if { [llength $party_id_list] != 0 } { + # Return after the first non-empty list + break + } + } + } + + if { [ad_conn isconnected] && [ad_conn user_id] != 0 } { + lappend party_id_list [ad_conn user_id] + } + + if { [llength $party_id_list] > 0 } { + set options [db_list_of_lists select_options {}] + } else { + set options {} + } + + set options [concat { { "Unassigned" "" } } $options] + lappend options { "Search..." ":search:"} + + return $options +} + +ad_proc -public workflow::case::role::get_seach_query { + {-case_id:required} + {-role_id:required} +} { + Get the search query for this role. + + @param case_id the ID of the case. + @param role_id the ID of the role. + + @author Lars Pind (lars@collaboraid.biz) +} { + set contract_name [workflow::service_contract::role_assignee_subquery] + + set impl_names [workflow::role::get_callbacks \ + -role_id $role_id \ + -contract_name $contract_name] + + set object_id [workflow::case::get_element -case_id $case_id -element object_id] + + set subquery {} + foreach impl_name $impl_names { + # Call the service contract implementation + set subquery [acs_sc::invoke \ + -contract $contract_name \ + -operation "GetSubquery" \ + -impl $impl_name \ + -call_args [list $case_id $object_id $role_id]] + + if { ![empty_string_p $subquery] } { + # Return after the first non-empty list + break + } + } + set query " + select distinct acs_object__name(p.party_id) || ' (' || p.email || ')' as label, p.party_id + from [ad_decode $subquery "" "cc_users" $subquery] p + where upper(coalesce(acs_object__name(p.party_id) || ' ', '') || p.email) like upper('%'||:value||'%') + order by label + " + return $query +} + +ad_proc -public workflow::case::role::get_assignee_widget { + {-case_id:required} + {-role_id:required} + {-prefix "role_"} +} { + Get the assignee widget for use with ad_form for this role. + + @param case_id the ID of the case. + @param role_id the ID of the role. + + @author Lars Pind (lars@collaboraid.biz) +} { + set workflow_id [workflow::case::get_element -case_id $case_id -element workflow_id] + + workflow::role::get -role_id $role_id -array role + set element "${prefix}$role(short_name)" + + set query [workflow::case::role::get_seach_query -case_id $case_id -role_id $role_id] + set picklist [workflow::case::role::get_picklist -case_id $case_id -role_id $role_id] + + return [list "${element}:search(search)" [list label $role(pretty_name)] [list mode display] \ + [list search_query $query] [list options $picklist] optional] +} + +ad_proc -public workflow::case::role::add_assignee_widgets { + {-case_id:required} + {-form_name:required} + {-prefix "role_"} +} { + Get the assignee widget for use with ad_form for this role. + + @param case_id the ID of the case. + @param role_id the ID of the role. + + @author Lars Pind (lars@collaboraid.biz) +} { + set workflow_id [workflow::case::get_element -case_id $case_id -element workflow_id] + set roles [list] + foreach role_id [workflow::get_roles -workflow_id $workflow_id] { + ad_form -extend -name $form_name -form [list [get_assignee_widget -case_id $case_id -role_id $role_id -prefix $prefix]] + } +} + +ad_proc -public workflow::case::role::set_assignee_values { + {-case_id:required} + {-form_name:required} + {-prefix "role_"} +} { + Get the assignee widget for use with ad_form for this role. + + @param case_id the ID of the case. + @param role_id the ID of the role. + + @author Lars Pind (lars@collaboraid.biz) +} { + set workflow_id [workflow::case::get_element -case_id $case_id -element workflow_id] + + # LARS TODO: + # Set role assignee values + foreach role_id [workflow::get_roles -workflow_id $workflow_id] { + workflow::role::get -role_id $role_id -array role + set element "${prefix}$role(short_name)" + + # HACK: Only care about the first assignee + set assignees [workflow::case::role::get_assignees -case_id $case_id -role_id $role_id] + if { [llength $assignees] == 0 } { + array set cur_assignee { party_id {} name {} email {} } + } else { + array set cur_assignee [lindex $assignees 0] + } + + if { [uplevel info exists bug:$element] } { + # Set normal value + if { [uplevel template::form is_request bug] || [string equal [uplevel [list element get_property bug $element mode]] "display"] } { + uplevel [list element set_value bug $element $cur_assignee(party_id)] + } + + # Set display value + if { [empty_string_p $cur_assignee(party_id)] } { + set display_value "None" + } else { + set display_value [acs_community_member_link \ + -user_id $cur_assignee(party_id) \ + -label $cur_assignee(name)] + + append display_value " ($cur_assignee(email))" + } + + uplevel [list element set_properties bug $element -display_value $display_value] + } + } +} + +ad_proc -public workflow::case::role::get_assignees { + {-case_id:required} + {-role_id:required} +} { + Get the current assignees for a role in a case as a list of + [array get]'s of party_id, email, name. + + @param case_id the ID of the case. + @param role_id the ID of the role. + + @author Lars Pind (lars@collaboraid.biz) +} { + set result {} + db_foreach select_assignees {} -column_array row { + lappend result [array get row] + } + return $result +} + ad_proc -public workflow::case::role::assignee_insert { {-case_id:required} {-role_id:required} - {-party_id:required} + {-party_ids:required} + {-replace:boolean} } { Insert a new assignee for this role @@ -306,36 +514,205 @@ @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 + db_transaction { + if { $replace_p } { + db_dml delete_assignees {} } + + foreach party_id $party_ids { + 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 + } + } + } } } +ad_proc -public workflow::case::role::assign { + {-case_id:required} + {-array:required} + {-replace:boolean} +} { + Assign roles from an array with entries like this: array(short_name) = [list of party_ids]. + + @param case_id The ID of the case. + @param array Name of array with assignment info + @param replace Should the new assignees replace existing assignees? + + @author Lars Pind (lars@collaboraid.biz) +} { + upvar $array assignees + + set workflow_id [workflow::case::get_element -case_id $case_id -element workflow_id] + + db_transaction { + foreach name [array names assignees] { + + set role_id [workflow::role::get_id \ + -workflow_id $workflow_id \ + -short_name $name] + + assignee_insert \ + -replace=$replace_p \ + -case_id $case_id \ + -role_id $role_id \ + -party_ids $assignees($name) + } + } +} + ad_proc -public workflow::case::get_activity_html { -case_id:required } { Get the activity log for a case as an HTML chunk } { - #LARS TODO: Template this + # LARS TODO: Template this + set workflow_id [workflow::case::get_element -case_id $case_id -element workflow_id] + set contract_name [workflow::service_contract::activity_log_format_title] + + # Get the name of any title Tcl callback proc + set impl_names [workflow::get_callbacks \ + -workflow_id $workflow_id \ + -contract_name $contract_name] + + # If there are more than one FormatLogTitle callback, we only use the first. + set impl_name [lindex $impl_names 0] + set log_html {} db_foreach select_log {} { - append log_html "$action_date_pretty $action_pretty_past_tense by $user_first_names $user_last_name -
[ad_html_text_convert -from $comment_format -to "text/html" -- $comment]
" + if { ![empty_string_p $impl_name] } { + set log_title [acs_sc::invoke \ + -contract $contract_name \ + -operation "GetTitle" \ + -impl $impl_name \ + -call_args [list $entry_id]] + set log_title [ad_decode $log_title "" "" "($log_title)"] + } + + append log_html "$creation_date_pretty $action_pretty_past_tense $log_title by $user_first_names $user_last_name +
[ad_html_text_convert -from $comment_mime_type -to "text/html" -- $comment]
" } return $log_html } +ad_proc workflow::case::get_notification_object { + {-type:required} + {-workflow_id ""} + {-case_id ""} +} { + Get the relevant object for this notification type. + @param type Type is one of 'workflow_assignee', 'workflow_my_cases', + 'workflow_case' (requires case_id), and 'workflow' (requires + workflow_id). +} { + switch $type { + workflow_case { + if { ![exists_and_not_null case_id] } { + return {} + } + return [workflow::case::get_element -case_id $case_id -element object_id] + } + workflow { + if { ![exists_and_not_null workflow_id] } { + return {} + } + return [workflow::get_element -workflow_id $workflow_id -element object_id] + } + default { + return [apm_package_id_from_key [workflow::package_key]] + } + } +} +ad_proc workflow::case::get_notification_request_url { + {-type:required} + {-workflow_id ""} + {-case_id ""} + {-return_url ""} + {-pretty_name ""} +} { + Get the URL to subscribe to notifications + + @param type Type is one of 'workflow_assignee', 'workflow_my_cases', + 'workflow_case' (requires case_id), and 'workflow' (requires + workflow_id). +} { + if { [ad_conn user_id] == 0 } { + return {} + } + + set object_id [get_notification_object \ + -type $type \ + -workflow_id $workflow_id \ + -case_id $case_id] + + if { [empty_string_p $object_id] } { + return {} + } + + if { ![exists_and_not_null return_url] } { + set return_url [util_get_current_url] + } + + set url [notification::display::subscribe_url \ + -type $type \ + -object_id $object_id \ + -url $return_url \ + -user_id [ad_conn user_id] \ + -pretty_name $pretty_name] + + return $url +} + +ad_proc workflow::case::get_notification_requests_multirow { + {-multirow_name:required} + {-label ""} + {-workflow_id ""} + {-case_id ""} + {-return_url ""} +} { + +} { + array set pretty { + workflow_assignee {my actions} + workflow_my_cases {my cases} + workflow_case {this case} + workflow {cases in this workflow} + } + + template::multirow create $multirow_name url label title + foreach type { + workflow_assignee workflow_my_cases workflow_case workflow + } { + set url [get_notification_request_url \ + -type $type \ + -workflow_id $workflow_id \ + -case_id $case_id \ + -return_url $return_url] + + if { ![empty_string_p $url] } { + set title "Subscribe to $pretty($type)" + if { ![empty_string_p $label] } { + set row_label $label + } else { + set row_label $title + } + template::multirow append $multirow_name $url $row_label $title + } + } +} + + + ##### # # workflow::case::fsm @@ -377,7 +754,7 @@ set row(entry_id) {} } else { db_1row select_case_info_after_action {} -column_array row - set row(entry_id) [db_nextval "workflow_case_log_seq"] + set row(entry_id) [db_nextval "acs_object_id_seq"] } } @@ -433,7 +810,7 @@ } if { !$permission_p } { - set privileges [workflow::action::get_privileges -action_id $action_id] + set privileges [concat "admin" [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 @@ -498,113 +875,212 @@ {-case_id:required} {-action_id:required} {-comment:required} - {-comment_format:required} + {-comment_mime_type:required} {-user_id} - {-no_check:boolean} + {-initial:boolean} {-entry_id {}} } { 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 (plain, text or html), according to - OpenACS standard text formatting (HM!) - @param user_id User_id - @param no_check Use this switch to bypass a check of whether the action is - enabled and the user is allowed to perform it. This - switch should normally not be used. - @param entry_id Optional entry_id for double-click protection. - This can be gotten from workflow::case::fsm::get. - @return entry_id of the new log entry. + @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_mime_type MIME Type of the comment, according to + OpenACS standard text formatting + @param user_id The user who's executing the action + @param initial Use this switch to signal that this is the initial action. This causes + permissions/enabled checks to be bypasssed, and causes all roles to get assigned. + @param entry_id Optional item_id for double-click protection. If you call workflow::case::fsm::get + with a non-empty action_id, it will generate a new entry_id for you, which you can pass in here. + @return entry_id of the new log entry (will be a cr_item). + @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 { !$initial_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] + set new_state_id [workflow::action::fsm::get_new_state -action_id $action_id] db_transaction { # Update the workflow state - if { ![empty_string_p $new_state] } { + if { ![empty_string_p $new_state_id] } { db_dml update_fsm_state {} } - # Maybe get entry_id, if one wasn't supplied - if { [empty_string_p $entry_id] } { - set entry_id [db_nextval "workflow_case_log_seq"] + # Double-click protection + if { ![empty_string_p $entry_id] } { + if { [db_string log_entry_exists_p {}] } { + return $entry_id + } } - - # Check if the row already exists - set exists_p [db_string log_entry_exists_p {}] - if { $exists_p } { - return $entry_id - } - - # We can't have empty comment_format - if { [empty_string_p $comment_format] } { + # We can't have empty comment_mime_type + if { [empty_string_p $comment_mime_type] } { # We need a default value here - set comment_format "text/plain" + set comment_mime_type "text/plain" } # Insert activity log entry - db_dml insert_log_entry {} + set extra_vars [ns_set create] + oacs_util::vars_to_ns_set \ + -ns_set $extra_vars \ + -var_list { entry_id case_id action_id comment comment_mime_type } + set entry_id [package_instantiate_object \ + -creation_user $user_id \ + -extra_vars $extra_vars \ + -package_name "workflow_case_log_entry" \ + "workflow_case_log_entry"] + # Assign new enabled roles, if currently unassigned - workflow::case::assign_roles -case_id $case_id + workflow::case::assign_roles -all=$initial_p -case_id $case_id - # Fire side-effects, both action ones and workflow ones - # ... TODO ... + # Fire side-effects + do_side_effects \ + -case_id $case_id \ + -action_id $action_id \ + -entry_id $entry_id # Notifications - # ... TODO ... + notify \ + -case_id $case_id \ + -action_id $action_id \ + -entry_id $entry_id \ + -comment $comment \ + -comment_mime_type $comment_mime_type + } + + return $entry_id +} +ad_proc -public workflow::case::action::do_side_effects { + {-case_id:required} + {-action_id:required} + {-entry_id:required} +} { + Fire the side-effects for this action +} { + set contract_name [workflow::service_contract::action_side_effect] - # LARS: TODO - # Taken from bug-tracker - if 0 { - # Setup any assignee for alerts on the bug - if { [info exists row(assignee)] && ![empty_string_p $row(assignee)] } { - bug_tracker::add_instant_alert \ - -bug_id $bug_id \ - -user_id $row(assignee) - } - } + # Get info for the callbacks + set workflow_id [workflow::case::get_element \ + -case_id $case_id \ + -element workflow_id] + # Get the callbacks, workflow and action + set impl_names [workflow::get_callbacks \ + -workflow_id $workflow_id \ + -contract_name $contract_name] + + set impl_names [concat $impl_names [workflow::action::get_callbacks \ + -action_id $action_id \ + -contract_name $contract_name]] - # LARS: TODO - # Taken from bug-tracker - if 0 { - set resolution {} - if { [exists_and_not_null row(resolution)] } { - set resolution $row(resolution) - } - - # Send out notifications - bug_tracker::bug_notify \ - -bug_id $bug_id \ - -action $action \ - -comment $description \ - -comment_format $desc_format \ - -resolution $resolution + if { [llength $impl_names] == 0 } { + return } + set object_id [workflow::case::get_element \ + -case_id $case_id \ + -element object_id] + + # Invoke them + foreach impl_name $impl_names { + acs_sc::invoke \ + -contract $contract_name \ + -operation "DoSideEffect" \ + -impl $impl_name \ + -call_args [list $case_id $object_id $action_id $entry_id] + } +} + +ad_proc -public workflow::case::action::notify { + {-case_id:required} + {-action_id:required} + {-entry_id:required} + {-comment:required} + {-comment_mime_type:required} +} { + Send out notifications to relevant people. +} { + # LARS TODO: + # Not implemented yet + return + + # Get workflow_id + workflow::case::get \ + -case_id $case_id \ + -array case + + workflow::get \ + -workflow_id $workflow_id \ + -array workflow + + # LARS TODO: + # we probably need a callback to format the message... + set subject "New notification" + set body "Here's the body" + + # LARS TODO: + # List of user_id's for people who are assigned to some task + # Don't forget to map parties to users + set assignee_list [list] + + # List of users who play some role in this case + set case_player_list [list] + + # LARS TODO: + # We want the subject/body to be customized depending on the type of notification + + foreach type { + workflow_assignee workflow_my_cases workflow_case workflow + } { + set subject($type) $subject + set body($type) $body + set force_p($type) 0 + set intersection($type) {} } + + set force_p(workflow_assignee) 1 + set intersection(workflow_assignee) $assignee_list + set intersection(workflow_my_cases) $case_player_list + - return $entry_id + set notified_list [list] + + foreach type { + workflow_assignee workflow_my_cases workflow_case workflow + } { + set object_id [get_notification_object \ + -type $type \ + -workflow_id $workflow_id \ + -case_id $case_id] + + if { ![empty_string_p $object_id] } { + set notified_list [notification::new \ + -type_id [notification::type::get_type_id -short_name $type] \ + -object_id $object_id \ + -response_id $case(object_id) \ + -notif_subject $subject($type) \ + -notif_text $body($type) \ + -already_notified $notified_list \ + -intersection $intersection($type) \ + -force=$force_p($type)] + } + } } + ##### # # workflow::case::action::fsm @@ -615,17 +1091,17 @@ {-case_id:required} {-action_id:required} } { - Get the new state which the workflow will be in after a certain action. + Get the ID of the new state which the workflow will be in after a certain 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] + set new_state_id [workflow::action::fsm::get_new_state -action_id $action_id] + if { [empty_string_p $new_state_id] } { + set new_state_id [workflow::case::fsm::get_current_state -case_id $case_id] } - return $new_state + return $new_state_id } 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 -r1.4 -r1.5 --- openacs-4/packages/workflow/tcl/case-procs.xql 3 Feb 2003 12:23:01 -0000 1.4 +++ openacs-4/packages/workflow/tcl/case-procs.xql 12 Feb 2003 14:23:15 -0000 1.5 @@ -74,34 +74,8 @@
- + - select l.entry_id, - l.case_id, - l.action_id, - a.short_name as action_short_name, - a.pretty_name as action_pretty_name, - a.pretty_past_tense as action_pretty_past_tense, - l.user_id, - u.first_names as user_first_names, - u.last_name as user_last_name, - u.email as user_email, - l.action_date, - to_char(l.action_date, 'fmMM/DDfm/YYYY') as action_date_pretty, - l.comment, - l.comment_format - from workflow_case_log l, - workflow_actions a, - cc_users u - where l.case_id = :case_id - and a.action_id = l.action_id - and u.user_id = l.user_id - - - - - - select impl.impl_name from workflow_role_callbacks r, acs_sc_impls impl, @@ -116,6 +90,23 @@ + + + select acs_object__name(p.party_id) || ' (' || p.email || ')' as label, p.party_id + from parties p + where p.party_id in ([join $party_id_list ", "]) + order by label + + + + + + delete from workflow_case_role_party_map + where case_id = :case_id + and role_id = :role_id + + + insert into workflow_case_role_party_map @@ -200,30 +191,16 @@ update workflow_case_fsm - set current_state = :new_state + set current_state = :new_state_id 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) - - - select count(*) - from workflow_case_log - where entry_id = :entry_id - and case_id = :case_id - and action_id = :action_id - and user_id = :user_id - and comment = :comment - and comment_format = :comment_format + from cr_revisions + where revision_id = :entry_id Index: openacs-4/packages/workflow/tcl/implementation-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/implementation-procs.tcl,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/implementation-procs.tcl 3 Feb 2003 12:23:01 -0000 1.1 +++ openacs-4/packages/workflow/tcl/implementation-procs.tcl 12 Feb 2003 14:23:15 -0000 1.2 @@ -15,6 +15,11 @@ namespace eval workflow::impl::role_assignee_pick_list {} namespace eval workflow::impl::role_assignee_pick_list::current_assignees {} +namespace eval workflow::impl::role_assignee_subquery {} +namespace eval workflow::impl::role_assignee_subquery::registered_users {} + +namespace eval workflow::impl::notification {} + ##### # # Generic service contract implementation procs @@ -67,7 +72,6 @@ } { Return the static assignees for this role } { - error "IMPL: [db_list select_static_assignees {}]" return [db_list select_static_assignees {}] } @@ -89,4 +93,48 @@ Return the list of current assignees for this case and role } { return [db_list select_current_assignees {}] -} \ No newline at end of file +} + + + + +##### +# +# Search Subquery - registered users +# +##### + +ad_proc -public workflow::impl::role_assignee_subquery::registered_users::pretty_name {} { + return "All registered users" +} + +ad_proc -public workflow::impl::role_assignee_subquery::registered_users::get_subquery { + case_id + object_id + role_id +} { + Return a subquery for all registered users. +} { + return [db_map cc_users] +} + + + +##### +# +# Notifications +# +##### + +ad_proc -public workflow::impl::notification::get_url { + object_id +} { + # Todo: Implement this proc +} + +ad_proc -public workflow::impl::notification::process_reply { + reply_id +} { + # Todo: Implement this proc +} + Index: openacs-4/packages/workflow/tcl/implementation-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/implementation-procs.xql,v diff -u -N -r1.1 -r1.2 --- openacs-4/packages/workflow/tcl/implementation-procs.xql 3 Feb 2003 12:23:01 -0000 1.1 +++ openacs-4/packages/workflow/tcl/implementation-procs.xql 12 Feb 2003 14:23:15 -0000 1.2 @@ -17,14 +17,21 @@ - - + - select party_id - from workflow_case_role_party_map - where role_id = :role_id - and case_id = :case_id + select m.party_id + from workflow_case_role_party_map m, + workflow_cases c + where m.role_id = :role_id + and m.case_id = c.case_id + and c.workflow_id = (select workflow_id from workflow_cases where case_id = :case_id) + + + cc_users + + + 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 -r1.3 -r1.4 --- openacs-4/packages/workflow/tcl/install-procs.tcl 3 Feb 2003 12:23:01 -0000 1.3 +++ openacs-4/packages/workflow/tcl/install-procs.tcl 12 Feb 2003 14:23:15 -0000 1.4 @@ -82,7 +82,7 @@ acs_sc::contract::delete -name [workflow::service_contract::role_assignee_subquery] acs_sc::contract::delete -name [workflow::service_contract::action_side_effect] - + acs_sc::contract::delete -name [workflow::service_contract::activity_log_format_title] } @@ -165,20 +165,19 @@ iscachable_p "t" } GetPrettyName { - description "Get the pretty name of this implementation. Will be localized, so i may contain #...#." + description "Get the pretty name of this implementation. Will be localized, so it may contain #...#." output { pretty_name:string } iscachable_p "t" } - GetSubQueryName { - description "Get the Query Dispatcher query name of the query which will return the list of parties who can be assigned to the role, and optionally bind variables to be filled in. Names of bind variables cannot start with an underscore (_)." + GetSubquery { + description "Get a subquery which will return the list of parties who can be assigned to the role, e.g. simply the name of a view of users/parties, or a subquery enclosed in parenthesis such as '(select * from parties where ...)'" input { case_id:integer object_id:integer role_id:integer } output { - subquery_name:string - bind:string,multiple + subquery:string } } } @@ -271,7 +270,14 @@ workflow::install::register_default_assignees_static_assignee_impl workflow::install::register_pick_list_current_assignee_impl + + workflow::install::register_search_query_registered_users_impl + + workflow::install::register_notification_impl + + workflow::install::register_notification_types } + } ad_proc -private workflow::install::unregister_implementations {} { @@ -291,6 +297,14 @@ acs_sc::impl::delete \ -contract_name [workflow::service_contract::role_assignee_pick_list] \ -impl_name "Role_PickList_CurrentAssignees" + + acs_sc::impl::delete \ + -contract_name [workflow::service_contract::role_assignee_subquery] \ + -impl_name "Role_AssigneeSubquery_RegisteredUsers" + + acs_sc::impl::delete \ + -contract_name "NotificationType" \ + -impl_name "WorkflowNotificationType" } } @@ -334,13 +348,91 @@ name "Role_PickList_CurrentAssignees" aliases { GetObjectType workflow::impl::acs_object - GetPrettyName workflow::impl::role_assignee_pick_list::pretty_name - GetPickList workflow::impl::role_assignee_pick_list::get_pick_list + GetPrettyName workflow::impl::role_assignee_pick_list::current_assignees::pretty_name + GetPickList workflow::impl::role_assignee_pick_list::current_assignees::get_pick_list } } lappend spec contract_name [workflow::service_contract::role_assignee_pick_list] lappend spec owner [workflow::package_key] acs_sc::impl::new_from_spec -spec $spec -} \ No newline at end of file +} + +ad_proc -private workflow::install::register_search_query_registered_users_impl {} { + + set spec { + name "Role_AssigneeSubquery_RegisteredUsers" + aliases { + GetObjectType workflow::impl::acs_object + GetPrettyName workflow::impl::role_assignee_subquery::registered_users::pretty_name + GetSubquery workflow::impl::role_assignee_subquery::registered_users::get_subquery + } + } + + lappend spec contract_name [workflow::service_contract::role_assignee_subquery] + lappend spec owner [workflow::package_key] + + acs_sc::impl::new_from_spec -spec $spec +} + +ad_proc -private workflow::install::register_notification_impl {} { + + set spec { + contract_name "NotificationType" + name "WorkflowNotificationType" + aliases { + GetURL workflow::impl::notification::get_url + ProcessReply workflow::impl::notification::process_reply + } + } + + lappend spec owner [workflow::package_key] + + acs_sc::impl::new_from_spec -spec $spec +} + + +##### +# +# Notifications +# +##### + +ad_proc -public workflow::install::register_notification_types {} { + Register workflow notification types +} { + set sc_impl_id [acs_sc::impl::get_id -owner [workflow::package_key] -name "WorkflowNotificationType"] + + set type_id [list] + + lappend type_ids [notification::type::new \ + -sc_impl_id $sc_impl_id \ + -short_name "workflow_assignee" \ + -pretty_name "Workflow Assignee" \ + -description "Notification of people who are assigned to an action in a workflow."] + + lappend type_ids [notification::type::new \ + -sc_impl_id $sc_impl_id \ + -short_name "workflow_my_cases" \ + -pretty_name "Workflow My Cases" \ + -description "Notification on all activity in any case you're participating in."] + + lappend type_ids [notification::type::new \ + -sc_impl_id $sc_impl_id \ + -short_name "workflow_case" \ + -pretty_name "Workflow Case" \ + -description "Notification on all activity in a specific case that you're interested in."] + + lappend type_ids [notification::type::new \ + -sc_impl_id $sc_impl_id \ + -short_name "workflow" \ + -pretty_name "Workflow" \ + -description "Notification on all activity in any case in a particular workflow (typically an instance of a package)."] + + # Enable all available intervals and delivery methods + foreach type_id $type_ids { + db_dml enable_all_intervals {} + db_dml enable_all_delivery_methods {} + } +} Index: openacs-4/packages/workflow/tcl/install-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/install-procs.xql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/install-procs.xql 12 Feb 2003 14:23:15 -0000 1.1 @@ -0,0 +1,24 @@ + + + + + + insert into notification_types_intervals + (type_id, interval_id) + select :type_id, interval_id + from notification_intervals; + + + + + + insert into notification_types_del_methods + (type_id, delivery_method_id) + select :type_id, delivery_method_id + from notification_delivery_methods; + + + + + + 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.5 -r1.6 --- openacs-4/packages/workflow/tcl/role-procs.tcl 3 Feb 2003 12:23:01 -0000 1.5 +++ openacs-4/packages/workflow/tcl/role-procs.tcl 12 Feb 2003 14:23:15 -0000 1.6 @@ -92,6 +92,10 @@ -name $callback_name } } + + # Role info for the workflow is changed, need to flush + workflow::role::flush_cache -workflow_id $workflow_id + return $role_id } @@ -107,9 +111,42 @@ @author Lars Pind (lars@collaboraid.biz) } { - return [db_string select_role_id {} -default {}] + # Get role info from cache + array set role_data [workflow::role::get_all_info -workflow_id $workflow_id] + + foreach role_id $role_data(role_ids) { + array set one_role $role_data($role_id) + + if { [string equal $one_role(short_name) $short_name] } { + return $one_role(role_id) + } + } + + error "workflow::role::get_id role with short_name $short_name not found for workflow $workflow_id" } +ad_proc -public workflow::role::get_workflow_id { + {-role_id:required} +} { + Lookup the workflow_id of a certain role_id. + + @author Peter Marklund +} { + return [util_memoize \ + [list workflow::role::get_workflow_id_not_cached -role_id $role_id]] +} + +ad_proc -private workflow::role::get_workflow_id_not_cached { + {-role_id:required} +} { + This is a proc that should only be used internally by the workflow + API, applications should use workflow::role::get_workflow_id instead. + + @author Peter Marklund +} { + return [db_string select_workflow_id {}] +} + ad_proc -public workflow::role::get { {-role_id:required} {-array:required} @@ -119,13 +156,16 @@ @param role_id The ID of the workflow @param array Name of the array you want the info returned in - @author Lars Pind (lars@collaboraid.biz) + @Author Lars Pind (lars@collaboraid.biz) } { + set workflow_id [workflow::role::get_workflow_id -role_id $role_id] + upvar $array row - db_1row role_info {} -column_array row + # Get info about all roles for this workflow + array set role_data [workflow::role::get_all_info -workflow_id $workflow_id] - set row(callbacks) [db_list role_callbacks {}] + array set row $role_data($role_id) } ad_proc -public workflow::role::get_element { @@ -143,6 +183,26 @@ return $row($element) } +ad_proc -private workflow::role::get_callbacks { + {-role_id:required} + {-contract_name:required} +} { + Get the impl_names of callbacks of a given contract for a given role. + + @param role_id the ID of the role to assign. + @param contract_name the name of the contract + + @author Lars Pind (lars@collaboraid.biz) +} { + array set callback_impl_names [get_element -role_id $role_id -element callback_impl_names] + + if { [info exists callback_impl_names($contract_name)] } { + return $callback_impl_names($contract_name) + } else { + return {} + } +} + ad_proc -private workflow::role::parse_spec { {-workflow_id:required} {-short_name:required} @@ -208,6 +268,10 @@ array unset row role_id array unset row workflow_id array unset row sort_order + array unset row role_ids + array unset row callbacks_array + array unset row callback_ids + array unset row callback_impl_names # Get rid of empty strings foreach name [array names row] { @@ -236,16 +300,16 @@ @author Lars Pind (lars@collaboraid.biz) } { # roles(short_name) { ... role-spec ... } - set roles [list] + array set roles [list] foreach role_id [workflow::get_roles -workflow_id $workflow_id] { - lappend roles [get_element -role_id $role_id -element short_name] [generate_spec -role_id $role_id] + lappend roles_list [get_element -role_id $role_id -element short_name] [generate_spec -role_id $role_id] } - - return $roles + + return $roles_list } -ad_proc -private workflow::role::callback_insert { +ad_proc -public workflow::role::callback_insert { {-role_id:required} {-name:required} {-sort_order} @@ -275,3 +339,98 @@ return $acs_sc_impl_id } + +ad_proc -private workflow::role::flush_cache { + {-workflow_id:required} +} { + Flush all caches related to roles for the given + workflow. Used internally by the workflow API only. + + @author Peter Marklund +} { + # TODO: Flush request cache + # ... + + # Flush the thread global cache + util_memoize_flush [list workflow::role::get_all_info_not_cached -workflow_id $workflow_id] +} + +ad_proc -private workflow::role::get_all_info { + {-workflow_id:required} +} { + This proc is for internal use in the workflow API only. + Returns all information related to roles for a certain + workflow instance. Uses util_memoize to cache values. + + @see workflow::role::get_all_info_not_cached + + @author Peter Marklund +} { + return [util_memoize [list workflow::role::get_all_info_not_cached \ + -workflow_id $workflow_id] [workflow::cache_timeout]] +} + +ad_proc -private workflow::role::get_all_info_not_cached { + {-workflow_id:required} +} { + This proc is for internal use in the workflow API only and + should not be invoked directly from application code. Returns + all information related to roles for a certain workflow instance. + Goes to the database on every invocation and should be used together + with util_memoize. + + @author Peter Marklund +} { + # For performance we avoid nested queries in this proc + set role_ids [list] + + db_foreach role_info {} -column_array row { + set role_id $row(role_id) + + lappend role_ids $role_id + + # store in role,$role_id arrays + foreach name [array names row] { + set role,${role_id}($name) $row($name) + } + + # Cache the mapping role_id -> workflow_id + util_memoize_seed \ + [list workflow::role::get_workflow_id_not_cached -role_id $role_id] \ + $workflow_id + } + unset row + + # Get the callbacks of all roles of the workflow + foreach role_id $role_ids { + set role,${role_id}(callbacks) {} + set role,${role_id}(callback_ids) {} + array set callback_impl_names,$role_id [list] + array set callbacks_array,$role_id [list] + } + + db_foreach role_callbacks {} -column_array row { + set role_id $row(role_id) + + lappend role,${role_id}(callbacks) "$row(impl_owner_name).$row(impl_name)" + lappend role,${role_id}(callback_ids) $row(impl_id) + + lappend callback_impl_names,${role_id}(${row(contract_name)}) $row(impl_name) + set callbacks_array,${role_id}($row(impl_id)) [array get row] + } + unset row + + foreach role_id $role_ids { + set role,${role_id}(callback_impl_names) [array get callback_impl_names,$role_id] + set role,${role_id}(callbacks_array) [array get callbacks_array,$role_id] + } + + # Build up the master role_data array + foreach role_id $role_ids { + set role_data($role_id) [array get role,$role_id] + } + + set role_data(role_ids) $role_ids + + return [array get role_data] +} 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.5 -r1.6 --- openacs-4/packages/workflow/tcl/role-procs.xql 3 Feb 2003 12:23:01 -0000 1.5 +++ openacs-4/packages/workflow/tcl/role-procs.xql 12 Feb 2003 14:23:15 -0000 1.6 @@ -28,35 +28,46 @@ - + - select role_id - from workflow_roles - where workflow_id = :workflow_id - and short_name = :short_name + select workflow_id + from workflow_roles + where role_id = :role_id - + select role_id, workflow_id, short_name, pretty_name, sort_order from workflow_roles - where role_id = :role_id + where workflow_id = :workflow_id + order by sort_order - + - select impl.impl_owner_name || '.' || impl.impl_name - from acs_sc_impls impl, - workflow_role_callbacks c - where c.role_id = :role_id + select c.role_id, + impl.impl_id, + impl.impl_owner_name, + impl.impl_name, + ctr.contract_name, + c.sort_order + from workflow_roles r, + workflow_role_callbacks c, + acs_sc_impls impl, + acs_sc_bindings bind, + acs_sc_contracts ctr + where r.workflow_id = :workflow_id + and c.role_id = r.role_id and impl.impl_id = c.acs_sc_impl_id - order by c.sort_order + and bind.impl_id = impl.impl_id + and ctr.contract_id = bind.contract_id + order by r.role_id, c.sort_order Index: openacs-4/packages/workflow/tcl/state-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/state-procs.tcl,v diff -u -N -r1.2 -r1.3 --- openacs-4/packages/workflow/tcl/state-procs.tcl 3 Feb 2003 12:23:01 -0000 1.2 +++ openacs-4/packages/workflow/tcl/state-procs.tcl 12 Feb 2003 14:23:15 -0000 1.3 @@ -52,6 +52,10 @@ db_dml do_insert {} } + + # State info for the workflow changed, flush whole state cache + workflow::state::flush_cache -workflow_id $workflow_id + return $state_id } @@ -99,10 +103,34 @@ return [db_string select_id {}] } +ad_proc -public workflow::state::fsm::get_workflow_id { + {-state_id:required} +} { + Lookup the workflow that the given state belongs to. + + @return The id of the workflow the state belongs to. + + @author Peter Marklund +} { + return [util_memoize \ + [list workflow::state::fsm::get_workflow_id_not_cached -state_id $state_id]] +} + ##### # Private procs ##### +ad_proc -private workflow::state::fsm::get_workflow_id_not_cached { + {-state_id:required} +} { + This proc is used internally by the workflow API only. Use the proc + workflow::state::fsm::get_workflow_id instead. + + @author Peter Marklund +} { + return [db_string select_workflow_id {}] +} + ad_proc -private workflow::state::fsm::parse_spec { {-workflow_id:required} {-short_name:required} @@ -198,11 +226,73 @@ @author Lars Pind (lars@collaboraid.biz) } { # states(short_name) { ... state-spec ... } - set states [list] + array set states [list] foreach state_id [workflow::fsm::get_states -workflow_id $workflow_id] { - lappend states [get_element -state_id $state_id -element short_name] [generate_spec -state_id $state_id] + lappend states_list [get_element -state_id $state_id -element short_name] [generate_spec -state_id $state_id] } - return $states + return $states_list + } + +ad_proc -private workflow::state::flush_cache { + {-workflow_id:required} +} { + Flush all caches related to state information for + the given workflow. Used internally by the workflow API + only. + + @author Peter Marklund +} { + # TODO: Flush request cache + # ... + + # Flush the thread global cache + util_memoize_flush [list workflow::state::get_all_info_not_cached -workflow_id $workflow_id] +} + +ad_proc -private workflow::state::fsm::get_all_info { + {-workflow_id:required} +} { + This proc is for internal use in the workflow API only. + Returns all information related to states for a certain + workflow instance. Uses util_memoize to cache values. + + @see workflow::state::fsm::get_all_info_not_cached + + @author Peter Marklund +} { + return [util_memoize [list workflow::state::fsm::get_all_info_not_cached \ + -workflow_id $workflow_id] [workflow::cache_timeout]] +} + +ad_proc -private workflow::state::fsm::get_all_info_not_cached { + {-workflow_id:required} +} { + This proc is for internal use in the workflow API only and + should not be invoked directly from application code. Returns + all information related to states for a certain workflow instance. + Goes to the database on every invocation and should be used together + with util_memoize. + + @author Peter Marklund +} { + array set state_data {} + + # Use a list to be able to retrieve states in sort order + set state_ids [list] + db_foreach select_states {} -column_array state_row { + # Cache the state_id -> workflow_id lookup + + util_memoize_seed \ + [list workflow::state::fsm::get_workflow_id_not_cached -state_id $state_row(state_id)] \ + $workflow_id + + set state_data($state_row(state_id)) [array get state_row] + lappend state_ids $state_row(state_id) + } + set state_data(state_ids) $state_ids + + return [array get state_data] +} Index: openacs-4/packages/workflow/tcl/state-procs.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/state-procs.xql,v diff -u -N -r1.2 -r1.3 --- openacs-4/packages/workflow/tcl/state-procs.xql 3 Feb 2003 12:23:01 -0000 1.2 +++ openacs-4/packages/workflow/tcl/state-procs.xql 12 Feb 2003 14:23:15 -0000 1.3 @@ -49,4 +49,25 @@ + + + select state_id, + sort_order, + short_name, + pretty_name, + hide_fields + from workflow_fsm_states + where workflow_id = :workflow_id + order by sort_order + + + + + + select workflow_id + from workflow_fsm_states + where state_id = :state_id + + + Index: openacs-4/packages/workflow/tcl/workflow-procs-oracle.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/workflow-procs-oracle.xql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/workflow/tcl/workflow-procs-oracle.xql 12 Feb 2003 14:23:15 -0000 1.1 @@ -0,0 +1,26 @@ + + + oracle8.1.7 + + + + workflow.new ( + :short_name, + :pretty_name, + :package_key, + :object_id, + :object_type, + :creation_user, + :creation_ip, + :context_id + ); + + + + + + acs_object.delete(:workflow_id) from dual; + + + + 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.5 -r1.6 --- openacs-4/packages/workflow/tcl/workflow-procs.tcl 3 Feb 2003 12:23:01 -0000 1.5 +++ openacs-4/packages/workflow/tcl/workflow-procs.tcl 12 Feb 2003 14:23:15 -0000 1.6 @@ -82,6 +82,11 @@ } } + # 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 } @@ -94,6 +99,8 @@ @author Peter Marklund } { + workflow::flush_cache -workflow_id $workflow_id + return [db_string do_delete {}] } @@ -109,26 +116,12 @@ @author Lars Pind (lars@collaboraid.biz) } { - if { [empty_string_p $package_key] } { - if { [empty_string_p $object_id] } { - if { [ad_conn isconnected] } { - set package_key [ad_conn package_key] - } else { - error "You must supply either package_key or object_id, or there must be a current connection" - set query_name select_workflow_id_by_package_key - } - } else { - set query_name select_workflow_id_by_object_id - } - } else { - if { [empty_string_p $object_id] } { - set query_name select_workflow_id_by_package_key - } else { - error "You must supply only one of either package_key or object_id" - } - } - set workflow_id [db_string $query_name {} -default {}] + 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]] + if { ![empty_string_p $workflow_id] } { return $workflow_id } else { @@ -140,23 +133,25 @@ {-workflow_id:required} {-array:required} } { - Return information about a workflow. + 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 info + @return An array list with keys workflow_id, short_name, + pretty_name, object_id, package_key, object_type, initial_action, + and callbacks. + } { # Select the info into the upvar'ed Tcl Array upvar $array row - db_1row workflow_info {} -column_array row - - set row(callbacks) [db_list workflow_callbacks {}] + array set row \ + [util_memoize [list workflow::get_not_cached -workflow_id $workflow_id] [workflow::cache_timeout]] } - ad_proc -public workflow::get_element { {-workflow_id:required} {-element:required} @@ -172,19 +167,6 @@ return $row($element) } -ad_proc -public workflow::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 -public workflow::get_roles { {-workflow_id:required} } { @@ -195,7 +177,10 @@ @author Lars Pind (lars@collaboraid.biz) } { - return [db_list select_role_ids {}] + # Use cached data about roles + array set role_data [workflow::role::get_all_info -workflow_id $workflow_id] + + return $role_data(role_ids) } ad_proc -public workflow::get_actions { @@ -208,7 +193,10 @@ @author Lars Pind (lars@collaboraid.biz) } { - return [db_list select_action_ids {}] + # Use cached data about actions + array set action_data [workflow::action::get_all_info -workflow_id $workflow_id] + + return $action_data(action_ids) } @@ -219,6 +207,101 @@ # Private procs ##### + + +ad_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 +} + +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 "" +} + +ad_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 { [empty_string_p $package_key] } { + if { [empty_string_p $object_id] } { + 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 { [empty_string_p $object_id] } { + 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 {}] +} + +ad_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] +} + ad_proc -private workflow::default_sort_order { {-workflow_id:required} {-table_name:required} @@ -261,11 +344,32 @@ # 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 } +ad_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 {} + } +} + + ##### # # workflow::fsm namespace @@ -300,6 +404,11 @@ } } + # 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 } @@ -350,6 +459,11 @@ 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 set spec [list] @@ -367,14 +481,17 @@ ad_proc -public workflow::fsm::get_states { {-workflow_id:required} } { - Get the state_id's of all the states in the workflow. + 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 [db_list select_state_ids {}] + # Use cached data + array set state_data [workflow::state::fsm::get_all_info -workflow_id $workflow_id] + + return $state_data(state_ids) } ad_proc -public workflow::fsm::get_initial_state { @@ -385,10 +502,13 @@ @author Peter Marklund } { - set initial_action_id [workflow::get_initial_action -workflow_id $workflow_id] + 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] + set initial_state [workflow::action::fsm::get_element \ + -action_id $initial_action_id \ + -element new_state_id] return $initial_state } @@ -491,10 +611,5 @@ } { set namev [split $name "."] - set impl_owner_name [lindex $namev 0] - set impl_name [lindex $namev 1] - - set acs_sc_impl_id [db_string select_impl_id {}] - - return $acs_sc_impl_id + return [acs_sc::impl::get_id -owner [lindex $namev 0] -name [lindex $namev 1]] } 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 -r1.4 -r1.5 --- openacs-4/packages/workflow/tcl/workflow-procs.xql 3 Feb 2003 12:23:01 -0000 1.4 +++ openacs-4/packages/workflow/tcl/workflow-procs.xql 12 Feb 2003 14:23:15 -0000 1.5 @@ -1,7 +1,7 @@ - + select workflow_id from workflows @@ -10,31 +10,45 @@ - + - select workflow_id, - short_name, - pretty_name, - object_id, - package_key, - object_type - from workflows - where workflow_id = :workflow_id + select w.workflow_id, + w.short_name, + w.pretty_name, + w.object_id, + w.package_key, + w.object_type, + a.short_name as initial_action, + a.action_id as initial_action_id + from workflows w, + workflow_initial_action wia, + workflow_actions a + where w.workflow_id = :workflow_id + and w.workflow_id = wia.workflow_id + and a.action_id = wia.action_id - + - select impl.impl_owner_name || '.' || impl.impl_name - from acs_sc_impls impl, - workflow_callbacks c - where c.workflow_id = :workflow_id - and impl.impl_id = c.acs_sc_impl_id - order by c.sort_order + select impl.impl_id, + impl.impl_name, + impl.impl_owner_name, + ctr.contract_name, + wc.sort_order + from workflow_callbacks wc, + acs_sc_impls impl, + acs_sc_bindings bind, + acs_sc_contracts ctr + where wc.workflow_id = :workflow_id + and impl.impl_id = wc.acs_sc_impl_id + and impl.impl_id = bind.impl_id + and bind.contract_id = ctr.contract_id + order by wc.sort_order - + select workflow_id from workflows @@ -44,32 +58,6 @@ - - - select action_id - from workflow_initial_action - where workflow_id = :workflow_id - - - - - - select role_id - from workflow_roles - where workflow_id = :workflow_id - order by sort_order - - - - - - select action_id - from workflow_actions - where workflow_id = :workflow_id - order by sort_order - - - select max(sort_order) @@ -93,22 +81,4 @@ - - - select state_id - from workflow_fsm_states - where workflow_id = :workflow_id - order by sort_order - - - - - - select impl_id - from acs_sc_impls - where impl_owner_name = :impl_owner_name - and impl_name = :impl_name - - - Index: openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl,v diff -u -N -r1.4 -r1.5 --- openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl 3 Feb 2003 12:23:13 -0000 1.4 +++ openacs-4/packages/workflow/tcl/test/workflow-test-procs.tcl 12 Feb 2003 14:24:02 -0000 1.5 @@ -17,6 +17,12 @@ return "bug_test" } +ad_proc workflow::test::initial_action_short_name {} { + The short name of the initial action of the test workflow +} { + return "open" +} + ad_proc workflow::test::workflow_object_id {} { } { @@ -100,11 +106,10 @@ [empty_string_p $user_roles] } -ad_proc workflow::test::workflow_setup_array_style {} { - Create a test workflow for the Bug Tracker +ad_proc workflow::test::workflow_get_array_style_spec {} { + Get the array-style spec for a workflow for the Bug Tracker Bug use case. } { - set spec { pretty_name "Bug Test" package_key "acs-automated-testing" @@ -114,7 +119,7 @@ submitter { pretty_name "Submitter" callbacks { - workflow.CreationUser + workflow.Role_DefaultAssignees_CreationUser } } assignee { @@ -160,7 +165,7 @@ resolve { pretty_name "Resolve" pretty_past_tense "Resolved" - assigned_roles { assignee } + assigned_role assignee enabled_states { open resolved } new_state "resolved" privileges { write } @@ -169,7 +174,7 @@ close { pretty_name "Close" pretty_past_tense "Closed" - assigned_roles { submitter } + assigned_role submitter enabled_states { resolved } new_state "closed" privileges { write } @@ -186,15 +191,75 @@ } set spec [list [workflow::test::workflow_name] $spec] + return $spec +} + +ad_proc workflow::test::workflow_setup_array_style {} { + Create a test workflow for the Bug Tracker + Bug use case. +} { # Cannot use bt_bug as we cannot assume Bug Tracker to be installed set workflow_id [workflow::fsm::new_from_spec \ -object_id [workflow::test::workflow_object_id] \ - -spec $spec] + -spec [workflow::test::workflow_get_array_style_spec]] return $workflow_id } +ad_proc workflow::test::array_lists_equal_p { list1 list2 } { + Are the two lists equal? +} { + set len1 [llength $list1] + set len2 [llength $list2] + + ns_log Notice "LARS2: lists_equal_p, len1=$len1, len2=$len2\nList1=$list1\nList2=$list2" + + if { $len1 != $len2 } { + return 0 + } + + if { $len1 == 1 } { + + # Single element list + + return [string equal [lindex $list1 0] [lindex $list2 0]] + } elseif { [expr $len1 % 2] == 0 } { + + # List, treat as array-list + + array set array1 $list1 + array set array2 $list2 + + foreach name [lsort [array names array1]] { + if { ![info exists array2($name)] } { + # Element in 1 doesn't exist in 2 + return 0 + } + + set elm1 $array1($name) + set elm2 $array2($name) + + if { ![array_lists_equal_p $elm1 $elm2] } { + return 0 + } + } + } else { + + # List, treat as normal list + + foreach elm1 $list1 elm2 $list2 { + ns_log Notice "LARS2: foreach, len1=[llength $elm1], len2=[llength $elm2]\nElm1=$elm1\nElm2=$elm2" + + if { ![array_lists_equal_p $elm1 $elm2] } { + return 0 + } + } + } + + return 1 +} + ad_proc workflow::test::workflow_setup {} { Create a test workflow for the Bug Tracker Bug use case. @@ -224,7 +289,7 @@ workflow::role::new -workflow_id $workflow_id \ -short_name "submitter" \ -pretty_name "Submitter" \ - -callbacks { workflow.CreationUser } + -callbacks { workflow.Role_DefaultAssignees_CreationUser } workflow::role::new -workflow_id $workflow_id \ -short_name "assignee" \ @@ -261,7 +326,7 @@ workflow::action::fsm::new \ -initial_action_p t \ -workflow_id $workflow_id \ - -short_name "open" \ + -short_name [workflow::test::initial_action_short_name] \ -pretty_name "Open" \ -pretty_past_tense "Opened" \ -new_state "open" @@ -289,7 +354,7 @@ -short_name "resolve" \ -pretty_name "Resolve" \ -pretty_past_tense "Resolved" \ - -assigned_role { assignee } \ + -assigned_role assignee \ -enabled_states { open resolved } \ -new_state "resolved" \ -privileges { write } \ @@ -300,7 +365,7 @@ -short_name "close" \ -pretty_name "Close" \ -pretty_past_tense "Closed" \ - -assigned_role { submitter } \ + -assigned_role submitter \ -enabled_states { resolved } \ -new_state "closed" \ -privileges { write } @@ -310,7 +375,7 @@ -short_name "reopen" \ -pretty_name "Reopen" \ -pretty_past_tense "Closed" \ - -allowed_roles { submitter } \ + -allowed_roles submitter \ -enabled_states { resolved closed } \ -new_state "open" \ -privileges { write } @@ -340,7 +405,7 @@ set case_id [workflow::case::new -workflow_id $workflow_id \ -object_id [workflow::test::workflow_object_id] \ -comment "Test workflow case" \ - -comment_format "plain" \ + -comment_mime_type "text/plain" \ -user_id [workflow::test::admin_owner_id]] return $case_id @@ -356,6 +421,15 @@ # if any catch did so in the executed code. # set error_p [catch workflow::test::workflow_setup error] set workflow_id [$create_proc] + + set generated_spec [workflow::fsm::generate_spec -workflow_id $workflow_id] + + ns_log Notice "LARS: Generated spec: $generated_spec" + ns_log Notice "LARS: Hard-coded spec: [workflow_get_array_style_spec]" + + aa_true "Checking that generated spec is identical to the spec that we created from (except for ordering)" \ + [array_lists_equal_p $generated_spec [workflow_get_array_style_spec]] + # Create the workflow case in open state set object_id [workflow::test::workflow_object_id] @@ -366,14 +440,48 @@ -object_id $object_id \ -workflow_short_name [workflow::test::workflow_name]] - #set retrieved_object_id \ - \# [workflow::case::get_object_id $case_id + # Test the workflow::get proc + workflow::get -workflow_id $workflow_id -array workflow_array + aa_equals "checking the short_name retrieved with workflow::get of workflow" \ + $workflow_array(short_name) \ + [workflow::test::workflow_name] + + set retrieved_initial_action_name [workflow::action::get_element \ + -action_id $workflow_array(initial_action_id) \ + -element short_name] + aa_equals "Checking initial action short name from workflow::get and workflow::action::get_element" \ + $retrieved_initial_action_name [workflow::test::initial_action_short_name] + + # Test changing the short_name and check that the flush is cached + # TODO... + + # Get the role short_names + set expect_role_names [list submitter assignee] + foreach role_id [workflow::get_roles -workflow_id $workflow_id] { + workflow::role::get -role_id $role_id -array role + + aa_true "checking that role names of workflow can be fetched with workflow::get_roles and workflow::role::get" \ + [expr [lsearch -exact $expect_role_names $role(short_name)] != -1] + + } + + # Get the action short names + set expect_action_names [list open comment edit resolve close reopen] + foreach action_id [workflow::get_actions -workflow_id $workflow_id] { + workflow::action::get -action_id $action_id -array action + + aa_true "checking retrieval of action names with workflow::get_actions and workflow::get" \ + [expr [lsearch -exact $expect_action_names $action(short_name)] != -1] + + } + + # Get the state short names + # TODO + aa_true "case_id of a created workflow case should be retrievable" \ [string equal $case_id $retrieved_case_id] - #aa_true "object_id of a created workflow case should be retrievable" \ - \# [string equal $object_id $retrieved_object_id] set expect_enabled_actions [list comment edit resolve] workflow::test::assert_case_state \ @@ -390,7 +498,7 @@ -action_id [workflow::action::get_id -workflow_id $workflow_id \ -short_name "resolve"] \ -comment "Resolving Bug" \ - -comment_format plain \ + -comment_mime_type "text/plain" \ -user_id [workflow::test::admin_owner_id] set expect_enabled_actions [list comment edit resolve reopen close] @@ -408,7 +516,7 @@ -action_id [workflow::action::get_id -workflow_id $workflow_id \ -short_name "close"] \ -comment "Closing Bug" \ - -comment_format plain \ + -comment_mime_type "text/plain" \ -user_id [workflow::test::admin_owner_id] set expect_enabled_actions [list comment edit reopen] @@ -424,13 +532,13 @@ set error_p [catch $test_chunk errMsg] + # Teardown + workflow::test::workflow_teardown + if { $error_p } { global errorInfo aa_false "error during setup: $errMsg - $errorInfo" $error_p } - - # Teardown - # workflow::test::workflow_teardown } @@ -466,11 +574,13 @@ @author Lars Pind @creation-date 22 January 2003 } { + set workflow_id_list [list] set test_chunk { set workflow_id_1 [workflow::test::workflow_setup] - + lappend workflow_id_list $workflow_id_1 set workflow_id_2 [workflow::fsm::clone -workflow_id $workflow_id_1 -object_id [workflow::test::workflow_object_id_2]] - + lappend workflow_id_list $workflow_id_2 + set spec_1 [workflow::fsm::generate_spec -workflow_id $workflow_id_1] set spec_2 [workflow::fsm::generate_spec -workflow_id $workflow_id_2] @@ -479,16 +589,15 @@ } set error_p [catch $test_chunk errMsg] - + + # Teardown + foreach workflow_id $workflow_id_list { + workflow::delete -workflow_id $workflow_id + } + if { $error_p } { global errorInfo aa_false "error during setup: $errMsg - $errorInfo" $error_p } - - catch { - workflow::delete -workflow_id $workflow_id_1 - workflow::delete -workflow_id $workflow_id_2 - } - }