Index: openacs-4/contrib/obsolete-packages/acs-content/acs-content.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-content/acs-content.info,v diff -u -r1.1 -r1.2 --- openacs-4/contrib/obsolete-packages/acs-content/acs-content.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/contrib/obsolete-packages/acs-content/acs-content.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Khy Huang Alpha Release 2001-03-06 @@ -21,8 +22,10 @@ - - + + + + Index: openacs-4/contrib/obsolete-packages/acs-util/acs-util.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-util/acs-util.info,v diff -u -r1.1 -r1.2 --- openacs-4/contrib/obsolete-packages/acs-util/acs-util.info 13 Mar 2001 22:59:27 -0000 1.1 +++ openacs-4/contrib/obsolete-packages/acs-util/acs-util.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + ACS Team A set of utility functions that supplement the ACS Kernel. 2000-11-09 @@ -20,9 +21,9 @@ - - - + + + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-util/sql/acs-util-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-util/sql/acs-util-package.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-util/sql/string-package.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/acs-util-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/acs-util-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/acs-util-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,3 @@ + +@@ acs-util-package +@@ string-package Index: openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/acs-util-package.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/acs-util-package.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/acs-util-package.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,98 @@ +--------------------------------------------------------------------- +-- A set of utilities dealing with various aspects of the repository +--------------------------------------------------------------------- + +create or replace package acs_util +is + +-- Determine if an item in some sort of hierarchy has children +function has_children ( + item_id_in IN varchar2, + table_name_in IN varchar2, + parent_column_in IN varchar2 +) return char; + +-- Perform a SQL query which will return some values +-- or NULL. Return 't' if a value was returned, 'f' otherwise +function boolean_query ( + query_in IN varchar2, + not_flag_in IN char := 'f' +) return char; + +-- Convert a SQL string to a TCL string: surround the string with +-- {} if it contains spaces +function sql_to_tcl_string ( + string_in IN varchar2 +) return varchar2; + +end acs_util; +/ +show errors + +create or replace package body acs_util +is + +function has_children ( + item_id_in IN varchar2, + table_name_in IN varchar2, + parent_column_in IN varchar2 +) return char +is + v_newline varchar2(10) := ' +'; + v_return char(1); +begin + + execute immediate 'select ''t'' ' || + ' from dual where exists (select 1 from ' || table_name_in || + ' where ' || parent_column_in || ' = ''' || item_id_in || ''')' + into + v_return; + + return 't'; + +exception + when no_data_found then + return 'f'; + +end has_children; + +function boolean_query ( + query_in IN varchar2, + not_flag_in IN char := 'f' +) return char +is + v_flag char(1); + v_sql_query varchar2(4000); +begin + v_sql_query := 'select ''t'' from dual where '; + + if not_flag_in <> 'f' then + v_sql_query := v_sql_query || 'not '; + end if; + + execute immediate v_sql_query || 'exists ( ' || query_in || ' )' into v_flag; + return 't'; + +exception + when no_data_found then + return 'f'; +end boolean_query; + +function sql_to_tcl_string ( + string_in IN varchar2 +) return varchar2 +is +begin + if instr(string_in, ' ') <> 0 then + return '{' || string_in || '}'; + else + return string_in; + end if; +end sql_to_tcl_string; + +end acs_util; +/ +show errors + + Index: openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/string-package.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/string-package.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-util/sql/oracle/string-package.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,120 @@ +-------------------------------------------------------- +-- Some generic functions and types to work with strings +-------------------------------------------------------- + +create or replace package str +is + + -- A record for tokenizing strings + type token_info is record ( + string varchar2(4000), -- The string we will be tokenizing + length integer, -- The length of the string + token_start integer, -- Start of last token + separators varchar2(4000) -- Characters which can separate tokens + ); + + -- Return the first token in a string, or null if there are + -- no more tokens + function first_token ( + string_in IN varchar2, + token_rec_in IN OUT NOCOPY token_info, + separators_in IN varchar2 := ' ' + ) return varchar2; + + -- Get the next token in the string, or null if there are + -- no more tokens + function next_token ( + token_rec_in IN OUT NOCOPY token_info + ) return varchar2; + + -- Determine if a string has more tokens to be returned + function has_more_tokens ( + token_rec_in IN OUT NOCOPY token_info + ) return char; + +end str; +/ +show errors + +create or replace package body str +is + +function first_token ( + string_in IN varchar2, + token_rec_in IN OUT NOCOPY token_info, + separators_IN IN varchar2 := ' ' +) return varchar2 +is +begin + + token_rec_in.string := string_in; + token_rec_in.length := length(string_in); + token_rec_in.token_start := 1; + token_rec_in.separators := separators_in; + + return next_token(token_rec_in); +end first_token; + + +function next_token ( + token_rec_in IN OUT NOCOPY token_info +) return varchar2 +is + v_token_start integer; +begin + + -- Check for string end + if token_rec_in.token_start > token_rec_in.length then + return null; + end if; + + -- Skip separators + while instr(token_rec_in.separators, + substr(token_rec_in.string, + token_rec_in.token_start, 1)) <> 0 + loop + + token_rec_in.token_start := token_rec_in.token_start + 1; + if token_rec_in.token_start > token_rec_in.length then + return null; + end if; + + end loop; + + v_token_start := token_rec_in.token_start; + + -- Skip until the next separator + while instr(token_rec_in.separators, + substr(token_rec_in.string, + token_rec_in.token_start, 1)) = 0 + loop + token_rec_in.token_start := token_rec_in.token_start + 1; + if token_rec_in.token_start > token_rec_in.length then + return substr(token_rec_in.string, v_token_start); + end if; + end loop; + + return substr(token_rec_in.string, v_token_start, + token_rec_in.token_start - v_token_start); + +end next_token; + +function has_more_tokens ( + token_rec_in IN OUT NOCOPY token_info +) return char +is +begin + if token_rec_in.token_start > token_rec_in.length then + return 'f'; + else + return 't'; + end if; +end has_more_tokens; + +end str; +/ +show errors + + + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/acs-workflow.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/acs-workflow.info,v diff -u -r1.1 -r1.2 --- openacs-4/contrib/obsolete-packages/acs-workflow/acs-workflow.info 13 Mar 2001 22:59:27 -0000 1.1 +++ openacs-4/contrib/obsolete-packages/acs-workflow/acs-workflow.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Lars Pind Provides generic workflow management features to all ACS objects. 2001-03-07 @@ -21,24 +22,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/acs-workflow-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/acs-workflow-drop.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/jobs-kill.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/jobs-start.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/load-workflow.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/sample-article-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/sample-article-drop.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/sample-expenses-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/contrib/obsolete-packages/acs-workflow/sql/sample-expenses-drop.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/acs-workflow-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/acs-workflow-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/acs-workflow-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,20 @@ +-- +-- acs-workflow/sql/acs-workflow-create.sql +-- +-- Calls the other SQL files to create the data models and PL/SQL packages. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: acs-workflow-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +@@ wf-core-create +@@ workflow-case-package +@@ workflow-package +@@ wf-callback-package +@@ jobs-start + +@@ sample-expenses-create +@@ sample-article-create Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/acs-workflow-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/acs-workflow-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/acs-workflow-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,18 @@ +-- +-- acs-workflow/sql/acs-workflow-drop.sql +-- +-- Drops the data model and the PL/SQL packages. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: acs-workflow-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + + +@@ jobs-kill +@@ wf-core-drop +drop package wf_callback; +drop package workflow; +drop package workflow_case; Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/jobs-kill.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/Attic/jobs-kill.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/jobs-kill.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,29 @@ + +declare + cursor timed_trans_cur is + select job from user_jobs where what = 'workflow_case.sweep_timed_transitions;'; + cursor hold_timeout_cur is + select job from user_jobs where what = 'workflow_case.sweep_hold_timeout;'; + job_no number; +begin + + open timed_trans_cur; + fetch timed_trans_cur into job_no; + if not timed_trans_cur%NOTFOUND then + dbms_job.remove( + job => job_no + ); + end if; + + open hold_timeout_cur; + fetch hold_timeout_cur into job_no; + if not timed_trans_cur%NOTFOUND then + dbms_job.remove( + job => job_no + ); + end if; + +end; +/ +show errors; + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/jobs-start.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/Attic/jobs-start.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/jobs-start.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,47 @@ + +declare + cursor timed_trans_cur is + select job from user_jobs where what = 'workflow_case.sweep_timed_transitions;'; + cursor hold_timeout_cur is + select job from user_jobs where what = 'workflow_case.sweep_hold_timeout;'; + job_no number; +begin + + open timed_trans_cur; + fetch timed_trans_cur into job_no; + if timed_trans_cur%NOTFOUND then + dbms_job.submit( + job => job_no, + what => 'workflow_case.sweep_timed_transitions;', + interval => 'sysdate + 1/24/4' + ); + else + dbms_job.change( + job => job_no, + what => 'workflow_case.sweep_timed_transitions;', + next_date => sysdate, + interval => 'sysdate + 1/24/4' + ); + end if; + + open hold_timeout_cur; + fetch hold_timeout_cur into job_no; + if timed_trans_cur%NOTFOUND then + dbms_job.submit( + job => job_no, + what => 'workflow_case.sweep_hold_timeout;', + interval => 'sysdate + 1/24/4' + ); + else + dbms_job.change( + job => job_no, + what => 'workflow_case.sweep_hold_timeout;', + next_date => sysdate, + interval => 'sysdate + 1/24/4' + ); + end if; + +end; +/ +show errors; + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/load-workflow.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/load-workflow.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/load-workflow.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,186 @@ +alter session set sql_trace = false +set autotrace off +set timing off + +create or replace function +my_random +return integer +is +begin + return dbms_random.random/power(2,32)+0.5; +end my_random; +/ +show errors + +create or replace procedure +push_task_ahead( + task_id integer, + state in varchar2, + user_id in integer, + workflow_key in varchar2, + transition_key in varchar2 +) +is + v_journal_id integer; + v_value char(1); +begin + if state = 'enabled' then + v_journal_id := workflow_case.task_action( + task_id => push_task_ahead.task_id, + action => 'start', + action_ip => '1.1.1.1', + user_id => push_task_ahead.user_id + ); + else + /* State must be started */ + if my_random < 0.02 then + v_journal_id := workflow_case.task_action( + task_id => push_task_ahead.task_id, + action => 'cancel', + action_ip => '1.1.1.1', + user_id => push_task_ahead.user_id + ); + else + v_journal_id := workflow_case.begin_task_action( + task_id => push_task_ahead.task_id, + action => 'finish', + action_ip => '1.1.1.1', + user_id => push_task_ahead.user_id + ); + + for attr_rec in (select a.attribute_name, datatype + from wf_transition_attribute_map m, acs_attributes a + where workflow_key = push_task_ahead.workflow_key + and transition_key = push_task_ahead.transition_key + and a.attribute_id = m.attribute_id) + loop + /* We only know how to handle boolean attributes ... but that's the only thing we have right now, so ... */ + if attr_rec.datatype = 'boolean' then + if my_random < 0.5 then + v_value := 't'; + else + v_value := 'f'; + end if; + + workflow_case.set_attribute_value( + journal_id => v_journal_id, + attribute_name => attr_rec.attribute_name, + value => v_value + ); + end if; + end loop; + + workflow_case.end_task_action( + journal_id => v_journal_id, + action => 'finish', + task_id => push_task_ahead.task_id + ); + end if; + end if; +end; +/ +show errors + + +declare + v_object_id integer; + v_workflow_key varchar2(100); + v_count integer; + v_num_cases integer; + v_party_id integer; + v_case_id integer; + v_task_id integer; + v_user_id integer; + v_num_tasks integer; + v_state varchar2(100); + v_transition_key varchar2(100); +begin + v_num_cases := 100; + dbms_random.initialize(943820482); + + /* Create a shit-load of cases */ + + for v_count in 1 .. v_num_cases + loop + + select object_id into v_object_id from acs_objects where rownum = 1; + + select workflow_key into v_workflow_key from wf_workflows where rownum = 1; + + v_case_id := workflow_case.new( + workflow_key => v_workflow_key, + object_id => v_object_id + ); + + for trans_rec in (select transition_key from wf_transitions where workflow_key = v_workflow_key) loop + for party_rec in (select party_id from parties sample(50)) loop + workflow_case.add_manual_assignment( + case_id => v_case_id, + transition_key => trans_rec.transition_key, + party_id => party_rec.party_id + ); + end loop; + end loop; + + workflow_case.start_case( + case_id => v_case_id + ); + + end loop; + + /* Move 85% of the cases all the way to finished */ + + for case_rec in (select case_id from wf_cases sample (85)) + loop + loop + select decode(count(*), 0, 0, 1) into v_num_tasks from wf_user_tasks; + exit when v_num_tasks = 0; + + if my_random < 0.005 then + workflow_case.cancel( + case_id => case_rec.case_id + ); + end if; + + select task_id, state, user_id, workflow_key, transition_key + into v_task_id, v_state, v_user_id, v_workflow_key, v_transition_key + from wf_user_tasks + where case_id = case_rec.case_id + and rownum = 1; + + push_task_ahead( + task_id => v_task_id, + state => v_state, + user_id => v_user_id, + workflow_key => v_workflow_key, + transition_key => v_transition_key + ); + end loop; + end loop; + + + /* Fire transitions at random */ + + for v_count in 1 .. round(v_num_cases * 0.15 * 3) + loop + select decode(count(*), 0, 0, 1) into v_num_tasks from wf_user_tasks; + exit when v_num_tasks = 0; + + select task_id, state, user_id, workflow_key, transition_key + into v_task_id, v_state, v_user_id, v_workflow_key, v_transition_key + from wf_user_tasks + where rownum = 1; + + push_task_ahead( + task_id => v_task_id, + state => v_state, + user_id => v_user_id, + workflow_key => v_workflow_key, + transition_key => v_transition_key + ); + end loop; +end; +/ +show errors + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-article-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-article-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-article-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,213 @@ +-- +-- acs-workflow/sql/sample-article-create.sql +-- +-- Creates a sample article-authoring workflow to play with +-- +-- @author Kevin Scaldeferri (kevin@theory.caltech.edu) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: sample-article-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +/* This table will hold one row for each case using this workflow. */ +create table wf_article_cases ( + case_id integer primary key + constraint wf_article_cases_case_fk + references wf_cases on delete cascade +); + + +declare + v_workflow_key varchar2(40); +begin + v_workflow_key := workflow.create_workflow( + workflow_key => 'article_wf', + pretty_name => 'Article Publication', + pretty_plural => 'Article Publications', + description => 'Workflow for managing the publication of an article', + table_name => 'wf_article_cases' + ); +end; +/ +show errors + +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('start', 'article_wf', 'Initial state', 1); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('to_be_written', 'article_wf', 'Needs to be written', 2); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('to_be_reviewed', 'article_wf', 'Needs review', 3); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('to_go_to_press', 'article_wf', 'Ready to go to press', 4); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('end', 'article_wf', 'End state', 5); + + +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('specification', 'Description and assignment by editor', 'article_wf', 1, 'user'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('writing', 'Writing by author', 'article_wf', 2, 'user'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('review', 'Approval by reviewer', 'article_wf', 3, 'user'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('press', 'Publication of article', 'article_wf', 4, 'automatic'); + + +-- specification -- +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('article_wf', 'specification', 'start', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('article_wf', 'specification', 'to_be_written', 'out'); + + +-- writing +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('article_wf', 'writing', 'to_be_written', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('article_wf', 'writing', 'to_be_reviewed', 'out'); + + +-- review (or-split) +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('article_wf', 'review', 'to_be_reviewed', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction, guard_callback, guard_custom_arg, guard_description) + values ('article_wf', 'review', 'to_go_to_press', 'out', 'wf_callback.guard_attribute_true', 'reviewer_ok', 'Reviewer approved article'); +insert into wf_arcs(workflow_key, transition_key, place_key, direction, guard_callback, guard_description) + values ('article_wf', 'review', 'to_be_written', 'out', '#', 'Reviewer disapproved article'); + + +-- press +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('article_wf', 'press', 'to_go_to_press', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('article_wf', 'press', 'end', 'out'); + + +declare + v_attribute_id acs_attributes.attribute_id%TYPE; +begin + v_attribute_id := workflow.create_attribute( + workflow_key => 'article_wf', + attribute_name => 'reviewer_ok', + datatype => 'boolean', + pretty_name => 'Reviewer Approval', + default_value => 'f' + ); + + insert into wf_transition_attribute_map + (workflow_key, transition_key, attribute_id, sort_order) + values + ('article_wf', 'review', v_attribute_id, 1); + +end; +/ +show errors; + + +-- assignment as part of workflow +insert into wf_transition_assignment_map + (workflow_key, transition_key, assign_transition_key) +values + ('article_wf', 'specification', 'writing'); + +insert into wf_transition_assignment_map + (workflow_key, transition_key, assign_transition_key) +values + ('article_wf', 'specification', 'review'); + + +/* Context stuff */ + + +insert into wf_context_transition_info ( + context_key, workflow_key, transition_key, hold_timeout_callback, hold_timeout_custom_arg +) values ( + 'default', 'article_wf', 'specification', 'wf_callback.time_sysdate_plus_x', 1/24 +); + +commit; + + +create or replace package wf_article_callback +is + procedure notification( + task_id in number, + custom_arg in varchar2, + party_to in integer, + party_from in out integer, + subject in out varchar2, + body in out varchar2 + ); + +end wf_article_callback; +/ +show errors + + +create or replace package body wf_article_callback +is + + procedure notification( + task_id in number, + custom_arg in varchar2, + party_to in integer, + party_from in out integer, + subject in out varchar2, + body in out varchar2 + ) + is + v_deadline_pretty varchar2(400); + v_object_name varchar2(4000); + v_transition_name wf_transitions.transition_name%TYPE; + v_name varchar2(1000); + begin + select to_char(ta.deadline,'Mon fmDDfm, YYYY HH24:MI:SS'), + acs_object.name(c.object_id), + tr.transition_name + into v_deadline_pretty, + v_object_name, + v_transition_name + from wf_tasks ta, wf_transitions tr, wf_cases c + where ta.task_id = notification.task_id + and c.case_id = ta.case_id + and tr.workflow_key = c.workflow_key + and tr.transition_key = ta.transition_key; + + subject := 'Assignment: '||v_transition_name||' '||v_object_name; + body := 'Dear '||acs_object.name(party_to)||' +'||' +Today, you have been assigned to a task. +'||' +Task : '||v_transition_name||' +Object : '||v_object_name||' +'; + + if v_deadline_pretty != '' then + body := body ||'Deadline: '||v_deadline_pretty||' +'; + end if; + end notification; + +end wf_article_callback; +/ +show errors + + + + +update wf_context_transition_info +set notification_callback = 'wf_article_callback.notification' +where workflow_key = 'article_wf' +and context_key = 'default' +and transition_key = 'specification'; + +commit; Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-article-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-article-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-article-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,27 @@ +-- +-- acs-workflow/sql/sample-article-drop.sql +-- +-- Drops the article-authoring workflow. +-- +-- @author Kevin Scaldeferri (kevin@theory.caltech.edu) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: sample-article-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +begin + workflow.delete_cases(workflow_key => 'article_wf'); +end; +/ +show errors; + +drop table wf_article_cases; + +begin + workflow.drop_workflow(workflow_key => 'article_wf'); +end; +/ +show errors; + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-expenses-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-expenses-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-expenses-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,276 @@ +-- +-- acs-workflow/sql/sample-expenses-create.sql +-- +-- Creates an expenses workflow to play with. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: sample-expenses-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- +/* This table will hold one row for each case using this workflow. */ +create table wf_expenses_cases ( + case_id integer primary key + constraint wf_expenses_cases_case_fk + references wf_cases on delete cascade +); + +declare + v_workflow_key varchar2(40); +begin + v_workflow_key := workflow.create_workflow( + workflow_key => 'expenses_wf', + pretty_name => 'Expense Authorization', + pretty_plural => 'Expense authorizations', + description => 'Workflow for authorizing employee''s expenses on the company''s behalf', + table_name => 'wf_expenses_cases' + ); +end; +/ +show errors + +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('start', 'expenses_wf', 'Initial state', 1); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('assignments_done', 'expenses_wf', 'Tasks have been assigned', 2); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('supervisor_to_approve', 'expenses_wf', 'Supervisor is to approve', 3); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('other_to_approve', 'expenses_wf', 'Other is to approve', 4); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('supervisor_approved', 'expenses_wf', 'Supervisor has approved', 5); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('other_approved', 'expenses_wf', 'Other has approved', 6); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('ready_to_buy', 'expenses_wf', 'Both have approved', 8); +insert into wf_places(place_key, workflow_key, place_name, sort_order) + values ('end', 'expenses_wf', 'End state', 9); + + +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('assign', 'Assign users to approval', 'expenses_wf', 1, 'user'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('and_split', 'Both supervisor and Other approval', 'expenses_wf', 2, 'automatic'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('supervisor_approval', 'Supervisor approval', 'expenses_wf', 3, 'user'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('other_approval', 'Other approval', 'expenses_wf', 4, 'user'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('and_join', 'Supervisor and other approval both done', 'expenses_wf', 5, 'automatic'); +insert into wf_transitions(transition_key, transition_name, workflow_key, sort_order, trigger_type) + values ('buy', 'Buy stuff', 'expenses_wf', 6, 'user'); + +-- assign -- +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'assign', 'start', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'assign', 'assignments_done', 'out'); + + +-- and-split -- +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'and_split', 'assignments_done', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'and_split', 'supervisor_to_approve', 'out'); +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'and_split', 'other_to_approve', 'out'); + + +-- supervisor_approval +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'supervisor_approval', 'supervisor_to_approve', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'supervisor_approval', 'supervisor_approved', 'out'); + + +-- other-approval +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'other_approval', 'other_to_approve', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'other_approval', 'other_approved', 'out'); + + +-- and-join and or-split +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'and_join', 'supervisor_approved', 'in'); +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'and_join', 'other_approved', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction, guard_callback, guard_description) + values ('expenses_wf', 'and_join', 'ready_to_buy', 'out', 'wf_expenses.guard_both_approved_p', 'Both supervisor and CEO approved'); +insert into wf_arcs(workflow_key, transition_key, place_key, direction, guard_callback, guard_description) + values ('expenses_wf', 'and_join', 'end', 'out', '#', 'Either supervisor or CEO did not approve'); + +-- buy +-- in +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'buy', 'ready_to_buy', 'in'); +-- out +insert into wf_arcs(workflow_key, transition_key, place_key, direction) + values ('expenses_wf', 'buy', 'end', 'out'); + + +declare + v_attribute_id acs_attributes.attribute_id%TYPE; +begin + v_attribute_id := workflow.create_attribute( + workflow_key => 'expenses_wf', + attribute_name => 'supervisor_ok', + datatype => 'boolean', + pretty_name => 'Supervisor Approval', + default_value => 'f' + ); + + insert into wf_transition_attribute_map + (workflow_key, transition_key, attribute_id, sort_order) + values + ('expenses_wf', 'supervisor_approval', v_attribute_id, 1); + + v_attribute_id := workflow.create_attribute( + workflow_key => 'expenses_wf', + attribute_name => 'other_ok', + datatype => 'boolean', + pretty_name => 'Other Approval', + default_value => 'f' + ); + + insert into wf_transition_attribute_map + (workflow_key, transition_key, attribute_id, sort_order) + values + ('expenses_wf', 'other_approval', v_attribute_id, 1); + +end; +/ +show errors; + +insert into wf_transition_assignment_map + (workflow_key, transition_key, assign_transition_key) +values + ('expenses_wf', 'assign', 'supervisor_approval'); + +insert into wf_transition_assignment_map + (workflow_key, transition_key, assign_transition_key) +values + ('expenses_wf', 'assign', 'other_approval'); + + + +/* Context stuff */ + +insert into wf_context_transition_info ( + context_key, workflow_key, transition_key, estimated_minutes +) values ( + 'default', 'expenses_wf', 'assign', 5 +); + +insert into wf_context_transition_info ( + context_key, workflow_key, transition_key, hold_timeout_callback, hold_timeout_custom_arg, estimated_minutes +) values ( + 'default', 'expenses_wf', 'supervisor_approval', 'wf_callback.time_sysdate_plus_x', 1/24, 15 +); + +insert into wf_context_transition_info ( + context_key, workflow_key, transition_key, estimated_minutes +) values ( + 'default', 'expenses_wf', 'other_approval', 15 +); + +insert into wf_context_transition_info ( + context_key, workflow_key, transition_key, estimated_minutes +) values ( + 'default', 'expenses_wf', 'buy', 30 +); + + +insert into wf_context_task_panels ( + context_key, workflow_key, transition_key, sort_key, header, template_url +) values ( + 'default', 'expenses_wf', 'supervisor_approval', 1, 'Claim Info', 'sample/expenses-claim-info' +); + +insert into wf_context_task_panels ( + context_key, workflow_key, transition_key, sort_key, header, template_url +) values ( + 'default', 'expenses_wf', 'supervisor_approval', 2, 'Logic and Aids', 'sample/expenses-approval-aids' +); + +insert into wf_context_task_panels ( + context_key, workflow_key, transition_key, sort_key, header, template_url +) values ( + 'default', 'expenses_wf', 'other_approval', 1, 'Claim Info', 'sample/expenses-claim-info' +); + +insert into wf_context_task_panels ( + context_key, workflow_key, transition_key, sort_key, header, template_url +) values ( + 'default', 'expenses_wf', 'other_approval', 2, 'Logic and Aids', 'sample/expenses-approval-aids' +); + +commit; + + + +/* Callbacks for the workflow */ + +create or replace package wf_expenses +is + + function guard_both_approved_p ( + case_id in number, + workflow_key in varchar2, + transition_key in varchar2, + place_key in varchar2, + direction_in varchar2, + custom_arg in varchar2 + ) + return char; + +end wf_expenses; +/ +show errors; + + +create or replace package body wf_expenses +is + function guard_both_approved_p ( + case_id in number, + workflow_key in varchar2, + transition_key in varchar2, + place_key in varchar2, + direction_in varchar2, + custom_arg in varchar2 + ) + return char + is + v_other_ok_p char(1); + v_supervisor_ok_p char(1); + begin + v_other_ok_p := workflow_case.get_attribute_value( + case_id => guard_both_approved_p.case_id, + attribute_name => 'other_ok' + ); + if v_other_ok_p = 'f' then + return 'f'; + end if; + v_supervisor_ok_p := workflow_case.get_attribute_value( + case_id => guard_both_approved_p.case_id, + attribute_name => 'supervisor_ok' + ); + return v_supervisor_ok_p; + end guard_both_approved_p; + +end wf_expenses; +/ +show errors; + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-expenses-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-expenses-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/sample-expenses-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,27 @@ +-- +-- acs-workflow/sql/sample-expenses-drop.sql +-- +-- Drops the expenses workflow. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: sample-expenses-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +begin + workflow.delete_cases(workflow_key => 'expenses_wf'); +end; +/ +show errors; + +drop table wf_expenses_cases; + +begin + workflow.drop_workflow(workflow_key => 'expenses_wf'); +end; +/ +show errors; + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade-4.0-4.0.1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade-4.0-4.0.1.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade-4.0-4.0.1.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,346 @@ +-- Recreating almost all foreign key constraints in order to add "on delete cascade" + +-- wf_workflows + +alter table wf_workflows drop constraint wf_workflows_workflow_key_fk; +alter table wf_workflows add ( + constraint wf_workflows_workflow_key_fk + foreign key(workflow_key) references acs_object_types(object_type) on delete cascade +); + +-- wf_places + +alter table wf_places drop constraint wf_place_workflow_fk; +alter table wf_places add ( + constraint wf_place_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +-- wf_transitions + +alter table wf_transitions drop constraint wf_transition_workflow_fk; +alter table wf_transitions add ( + constraint wf_transition_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +-- wf_arcs + +alter table wf_arcs drop constraint wf_ts_arc_workflow_fk; +alter table wf_arcs add ( + constraint wf_ts_arc_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_arcs drop constraint wf_arc_transition_fk; +alter table wf_arcs add ( + constraint wf_arc_transition_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +alter table wf_arcs drop constraint wf_arc_place_fk; +alter table wf_arcs add ( + constraint wf_arc_place_fk + foreign key(workflow_key, place_key) references wf_places(workflow_key, place_key) on delete cascade +); + +create index wf_arcs_wf_key_trans_key_idx on wf_arcs(workflow_key, transition_key); +create index wf_arcs_wf_key_place_key_idx on wf_arcs(workflow_key, place_key); + +-- wf_attribute_info + +alter table wf_attribute_info add ( + constraint wf_attribute_info_attribute_pk primary key(attribute_id) +); + +alter table wf_attribute_info drop constraint wf_attribute_info_attribute_fk; +alter table wf_attribute_info add ( + constraint wf_attribute_info_attribute_fk + foreign key(attribute_id) references acs_attributes(attribute_id) on delete cascade +); + +-- wf_transition_attribute_map + +alter table wf_transition_attribute_map drop constraint wf_trans_attr_map_workflow_fk; +alter table wf_transition_attribute_map add ( + constraint wf_trans_attr_map_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_transition_attribute_map drop constraint wf_trans_attr_map_trans_fk; +alter table wf_transition_attribute_map add ( + constraint wf_trans_attr_map_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +-- wf_transition_assignment_map + +alter table wf_transition_assignment_map drop constraint wf_trans_asgn_map_workflow_fk; +alter table wf_transition_assignment_map add ( + constraint wf_trans_asgn_map_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_transition_assignment_map drop constraint wf_trans_asgn_map_trans_fk; +alter table wf_transition_assignment_map add ( + constraint wf_trans_asgn_map_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +alter table wf_transition_assignment_map drop constraint wf_tr_asgn_map_asgn_trans_fk; +alter table wf_transition_assignment_map add ( + constraint wf_tr_asgn_map_asgn_trans_fk + foreign key(workflow_key, assign_transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_trans_asgn_map_wf_trans_idx on wf_transition_assignment_map(workflow_key, transition_key); +create index wf_trans_asgn_map_wf_as_tr_idx on wf_transition_assignment_map(workflow_key, assign_transition_key); + +-- wf_context_transition_info + +alter table wf_context_transition_info drop constraint wf_context_trans_trans_fk; +alter table wf_context_transition_info add ( + constraint wf_context_trans_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_ctx_trans_wf_trans_idx on wf_context_transition_info(workflow_key, transition_key); + +-- wf_context_assignments + +alter table wf_context_assignments drop constraint wf_context_assign_context_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_context_fk + foreign key(context_key) references wf_contexts(context_key) on delete cascade +); + +alter table wf_context_assignments drop constraint wf_context_assign_workflow_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_context_assignments drop constraint wf_context_assign_party_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_party_fk + foreign key(party_id) references parties(party_id) on delete cascade +); + +alter table wf_context_assignments drop constraint wf_context_assign_trans_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_ctx_assg_workflow_trans_idx on wf_context_assignments(workflow_key, transition_key); + +-- wf_cases + +alter table wf_cases drop constraint wf_cases_acs_object_fk; +alter table wf_cases add ( + constraint wf_cases_acs_object_fk + foreign key(case_id) references acs_objects(object_id) on delete cascade +); + +alter table wf_cases drop constraint wf_cases_workflow_fk; +alter table wf_cases add ( + constraint wf_cases_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_cases drop constraint wf_cases_context_fk; +alter table wf_cases add ( + constraint wf_cases_context_fk + foreign key(context_key) references wf_contexts(context_key) on delete cascade +); + +alter table wf_cases drop constraint wf_cases_object_fk; +alter table wf_cases add ( + constraint wf_cases_object_fk + foreign key(object_id) references acs_objects(object_id) on delete cascade +); + +create index wf_cases_workflow_key_idx on wf_cases(workflow_key); +create index wf_cases_context_key_idx on wf_cases(context_key); +create index wf_cases_object_id_idx on wf_cases(object_id); + +-- wf_case_assignments + +alter table wf_case_assignments drop constraint wf_case_assign_fk; +alter table wf_case_assignments add ( + constraint wf_case_assign_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_case_assignments drop constraint wf_case_assign_party_fk; +alter table wf_case_assignments add ( + constraint wf_case_assign_party_fk + foreign key(party_id) references parties(party_id) on delete cascade +); + +alter table wf_case_assignments drop constraint wf_case_assign_trans_fk; +alter table wf_case_assignments add ( + constraint wf_case_assign_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_case_assgn_party_idx on wf_case_assignments(party_id); + +-- wf_case_deadlines + +alter table wf_case_deadlines drop constraint wf_case_deadline_fk; +alter table wf_case_deadlines add ( + constraint wf_case_deadline_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_case_deadlines drop constraint wf_case_deadline_trans_fk; +alter table wf_case_deadlines add ( + constraint wf_case_deadline_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +-- wf_tasks + +alter table wf_tasks drop constraint wf_task_case_fk; +alter table wf_tasks add ( + constraint wf_task_case_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_tasks drop constraint wf_task_holding_user_fk; +alter table wf_tasks add ( + constraint wf_task_holding_user_fk + foreign key(holding_user) references users(user_id) on delete cascade +); + +create index wf_tasks_case_id_idx on wf_tasks(case_id); +create index wf_tasks_holding_user_idx on wf_tasks(holding_user); + +-- wf_task_assignments + +alter table wf_task_assignments drop constraint wf_task_assign_task_fk; +alter table wf_task_assignments add ( + constraint wf_task_assign_task_fk + foreign key(task_id) references wf_tasks(task_id) on delete cascade +); + +alter table wf_task_assignments drop constraint wf_task_party_fk; +alter table wf_task_assignments add ( + constraint wf_task_party_fk + foreign key(party_id) references parties(party_id) on delete cascade +); + +create index wf_task_asgn_party_id_idx on wf_task_assignments(party_id); + +-- wf_tokens + +alter table wf_tokens drop constraint wf_token_workflow_instance_fk; +alter table wf_tokens add ( + constraint wf_token_workflow_instance_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +create index wf_tokens_case_id_idx on wf_tokens(case_id); + +-- wf_attribute_value_audit + +alter table wf_attribute_value_audit drop constraint wf_attr_val_audit_case_fk; +alter table wf_attribute_value_audit add ( + constraint wf_attr_val_audit_case_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_attribute_value_audit drop constraint wf_attr_val_audit_attr_fk; +alter table wf_attribute_value_audit add ( + constraint wf_attr_val_audit_attr_fk + foreign key(attribute_id) references acs_attributes(attribute_id) on delete cascade +); + +create index wf_attr_val_aud_attr_id_idx on wf_attribute_value_audit(attribute_id); + + +-- Added a column to wf_tasks + +alter table wf_tasks add (estimated_minutes integer); + + +-- We've added the column estimated_minutes to this view. + +create or replace view wf_enabled_transitions as +select c.case_id, + t.transition_key, + t.transition_name, + t.workflow_key, + t.sort_order, + t.trigger_type, + t.context_key, + t.enable_callback, + t.enable_custom_arg, + t.fire_callback, + t.fire_custom_arg, + t.assignment_callback, + t.assignment_custom_arg, + t.time_callback, + t.time_custom_arg, + t.deadline_callback, + t.deadline_custom_arg, + t.deadline_attribute_name, + t.hold_timeout_callback, + t.hold_timeout_custom_arg, + t.estimated_minutes + from wf_transition_info t, + wf_cases c + where t.workflow_key = c.workflow_key + and t.context_key = c.context_key + and c.state = 'active' + and not exists + (select tp.place_key + from wf_transition_places tp + where tp.transition_key = t.transition_key + and tp.workflow_key = t.workflow_key + and tp.direction = 'in' + and not exists + (select tk.token_id + from wf_tokens tk + where tk.place_key = tp.place_key + and tk.case_id = c.case_id + and tk.state = 'free' + ) + ); + + +-- We've added the column workflow_key to this view + +create or replace view wf_user_tasks as +select distinct ta.task_id, + ta.case_id, + ta.workflow_key, + ta.transition_key, + tr.transition_name, + ta.enabled_date, + ta.started_date, + u.user_id, + ta.state, + ta.holding_user, + ta.hold_timeout, + ta.deadline, + ta.estimated_minutes +from wf_tasks ta, + wf_task_assignments tasgn, + wf_cases c, + wf_transition_info tr, + party_approved_member_map m, + users u +where ta.state in ( 'enabled','started') +and c.case_id = ta.case_id +and c.state = 'active' +and tr.transition_key = ta.transition_key +and tr.trigger_type = 'user' +and tr.context_key = c.context_key +and tasgn.task_id = ta.task_id +and m.party_id = tasgn.party_id +and u.user_id = m.member_id; + + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-callback-package.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-callback-package.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-callback-package.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,68 @@ +-- +-- acs-workflow/sql/wf-callback-package.sql +-- +-- Creates the PL/SQL package that provides a small library of reusable +-- workflow callback functions/procedures. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: wf-callback-package.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +create or replace package wf_callback +as + + function guard_attribute_true( + case_id in number, + workflow_key in varchar2, + transition_key in varchar2, + place_key in varchar2, + direction in varchar2, + custom_arg in varchar2 + ) return char; + + function time_sysdate_plus_x( + case_id in number, + transition_key in varchar2, + custom_arg in varchar2 + ) return date; + +end wf_callback; +/ +show errors; + +create or replace package body wf_callback +as + + function guard_attribute_true( + case_id in number, + workflow_key in varchar2, + transition_key in varchar2, + place_key in varchar2, + direction in varchar2, + custom_arg in varchar2 + ) return char + is + begin + return workflow_case.get_attribute_value( + case_id => case_id, + attribute_name => custom_arg + ); + end guard_attribute_true; + + function time_sysdate_plus_x( + case_id in number, + transition_key in varchar2, + custom_arg in varchar2 + ) return date + is + begin + return sysdate + to_number(custom_arg); + end time_sysdate_plus_x; + +end wf_callback; +/ +show errors; + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-core-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-core-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-core-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,809 @@ +-- +-- acs-workflow/sql/wf-core-create.sql +-- +-- Creates the data model and views for the workflow package. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: wf-core-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +---------------------------------- +-- KNOWLEDGE LEVEL OBJECTS +---------------------------------- + + +/* Create the workflow superclass */ + +begin + acs_object_type.create_type( + object_type => 'workflow', + pretty_name => 'Workflow', + pretty_plural => 'Workflow', + table_name => 'wf_cases', + id_column => 'case_id' + ); +end; +/ +show errors + +create table wf_workflows ( + workflow_key varchar2(100) + constraint wf_workflows_pk + primary key + constraint wf_workflows_workflow_key_fk + references acs_object_types(object_type) + on delete cascade, + description varchar2(4000) +); + +comment on table wf_workflows is ' + Parent table for the workflow definition. +'; + +create table wf_places ( + place_key varchar2(100), + workflow_key varchar2(100) + constraint wf_place_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + place_name varchar2(100) + constraint wf_place_name_nn + not null, + -- purely for UI purposes + sort_order integer + constraint wf_place_order_ck + check (sort_order > 0), + constraint wf_place_pk + primary key (workflow_key, place_key), + constraint wf_places_wf_key_place_name_un + unique (workflow_key, place_name) +); + +comment on table wf_places is ' + The circles of the petri net. These hold the tokens representing the overall + state of the workflow. +'; + +create table wf_transitions ( + transition_key varchar2(100), + transition_name varchar2(100) + constraint wf_transition_name_nn + not null, + workflow_key varchar2(100) + constraint wf_transition_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + -- purely for UI purposes + sort_order integer + constraint wf_transition_order_ck + check (sort_order > 0), + trigger_type varchar2(40) + constraint wf_transition_trigger_type_ck + check (trigger_type in + ('automatic','user','message','time')), + constraint wf_transition_pk + primary key (workflow_key, transition_key), + constraint wf_trans_wf_key_trans_name_un + unique (workflow_key, transition_name) +); + +comment on table wf_transitions is ' + The squares in the petri net. The things that somebody (or something) actually does. +'; + +create table wf_arcs ( + workflow_key varchar2(100) + constraint wf_ts_arc_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + transition_key varchar2(100), + place_key varchar2(100), + -- direction is relative to the transition + direction varchar2(3) + constraint wf_arc_direction_ck + check (direction in ('in','out')), + /* Must be satisfied for the arc to be traveled by a token + * This is the name of a PL/SQL function to execute, which must return t or f + * Signature: (case_id in number, workflow_key in varchar2, transition_key in varchar2, + * place_key in varchar2, direction in varchar2, custom_arg in varchar2) + * return char(1) + */ + guard_callback varchar2(100), + guard_custom_arg varchar2(4000), + guard_description varchar2(500), + constraint wf_arc_guard_on_in_arc_ck + check (guard_callback is null or direction = 'out'), + constraint wf_arc_transition_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade, + constraint wf_arc_place_fk + foreign key (workflow_key, place_key) references wf_places(workflow_key, place_key) + on delete cascade, + constraint wf_arc_pk + primary key (workflow_key, transition_key, place_key, direction) +); + +create index wf_arcs_wf_key_trans_key_idx on wf_arcs(workflow_key, transition_key); +create index wf_arcs_wf_key_place_key_idx on wf_arcs(workflow_key, place_key); + +comment on table wf_arcs is ' + The arcs of the workflow petri net. + Arcs always go between a transition and a place. + The direction is relative to the transition here, i.e. + in means it goes into the transition, out means it goes + away from the transition. +'; + +create table wf_attribute_info ( + attribute_id integer + constraint wf_attribute_info_attribute_pk + primary key + constraint wf_attribute_info_attribute_fk + references acs_attributes(attribute_id) + on delete cascade, + wf_datatype varchar2(50) + constraint wf_attr_info_wf_datatype_ck + check (wf_datatype in ('none', 'party')) +); + + +create table wf_transition_attribute_map ( + workflow_key varchar2(100) + constraint wf_trans_attr_map_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + transition_key varchar2(100), + -- so the user can decide in what order the attributes should be presented + sort_order integer not null, + attribute_id integer + constraint wf_trans_attr_map_attribute_fk + references acs_attributes, + constraint wf_trans_attr_map_pk + primary key (workflow_key, transition_key, attribute_id), + constraint wf_trans_attr_map_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade +); + +comment on table wf_transition_attribute_map is ' + The workflow attributes that should be set when + the given transition is fired. +'; + + +create table wf_transition_assignment_map ( + workflow_key varchar2(100) + constraint wf_trans_asgn_map_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + transition_key varchar2(100), + assign_transition_key varchar2(100), + constraint wf_trans_asgn_map_pk + primary key (workflow_key, transition_key, assign_transition_key), + constraint wf_trans_asgn_map_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade, + constraint wf_tr_asgn_map_asgn_trans_fk + foreign key (workflow_key, assign_transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade +); + +create index wf_trans_asgn_map_wf_trans_idx on wf_transition_assignment_map(workflow_key, transition_key); +create index wf_trans_asgn_map_wf_as_tr_idx on wf_transition_assignment_map(workflow_key, assign_transition_key); + +comment on table wf_transition_assignment_map is ' + When part of the output of one task is to assign users to another task, + specify that this is the case by inserting a row here. +'; + +comment on column wf_transition_assignment_map.transition_key is ' + transition_key is the assigning transition. +'; + +comment on column wf_transition_assignment_map.assign_transition_key is ' + transition_key is the transition being assigned a user to. +'; + + + + + +/* + * Contexts + */ + +create table wf_contexts ( + context_key varchar2(100) + constraint wf_context_pk + primary key, + context_name varchar2(100) + constraint wf_contexts_context_name_nn + not null + constraint wf_contexts_context_name_un + unique +); + +comment on table wf_contexts is ' + The context of a workflow holds everything that''s not directly + part of the Petri Net structure, the stuff that''s likely to + be changed as the workflow is applied in a real business, and that + you will want to customize across different departments of the + same business. It includes assignments of transitions to parties, + the call-backs, etc. +'; + +/* Insert a default context that all new cases will use if nothing else is defined */ +insert into wf_contexts (context_key, context_name) values ('default', 'Default Context'); + +commit; + + +create table wf_context_transition_info ( + context_key varchar2(100) + constraint wf_context_trans_context_fk + references wf_contexts, + workflow_key varchar2(100) + constraint wf_context_trans_workflow_fk + references wf_workflows, + transition_key varchar2(100), + /* information for the transition in the context */ + /* The number of minutes this task is estimated to take */ + estimated_minutes integer, + /* + * Will be called when the transition is enabled/fired. + * signature: (case_id in number, transition_key in varchar2, custom_arg in varchar2) + */ + enable_callback varchar2(100), + enable_custom_arg varchar2(4000), + fire_callback varchar2(100), + fire_custom_arg varchar2(4000), + /* + * Must insert rows into the wf_task_assignments table. + * Will be called when the transition becomes enabled + * signature: (task_id in number, custom_arg in varchar2) + */ + assignment_callback varchar2(100), + assignment_custom_arg varchar2(4000), + /* + * Must return the date that the timed transition should fire + * Will be called when the transition is enabled + * signature: (case_id in number, transition_key in varchar2, custom_arg in varchar2) return date + */ + time_callback varchar2(100), + time_custom_arg varchar2(4000), + /* + * Returns the deadline for this task. + * Will be called when the transition becomes enabled + * Signature: (case_id in number, transition_key in varchar2, custom_arg in varchar2) return date + */ + deadline_callback varchar2(100), + deadline_custom_arg varchar2(4000), + /* The name of an attribute that holds the deadline */ + deadline_attribute_name varchar2(100), + /* + * Must return the date that the user's hold on the task times out. + * called when the user starts the task. + * signature: (case_id in number, transition_key in varchar2, custom_arg in varchar2) return date + */ + hold_timeout_callback varchar2(100), + hold_timeout_custom_arg varchar2(4000), + /* + * Notification callback + * Will be called when a notification is sent i.e., when a transition is enabled, + * or assignment changes. + * signature: (task_id in number, + * custom_arg in varchar2, + * party_to in integer, + * party_from in out integer, + * subject in out varchar2, + * body in out varchar2) + */ + notification_callback varchar2(100), + notification_custom_arg varchar2(4000), + /* + * Unassigned callback + * Will be called whenever a task becomes unassigned + * Signature: (case_id in number, transition_key in varchar2, custom_arg in varchar2) + */ + unassigned_callback varchar2(100), + unassigned_custom_arg varchar2(4000), + /* name of the privilege we should check before allowing access + * to task information. + */ + access_privilege varchar2(100), + /* table constraints */ + constraint wf_context_trans_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade, + constraint wf_context_transition_pk + primary key (context_key, workflow_key, transition_key) +); + +create index wf_ctx_trans_wf_trans_idx on wf_context_transition_info(workflow_key, transition_key); + +comment on table wf_context_transition_info is ' + This table holds information that pertains to a transition in a specific context. + It will specifically hold +'; + + +create table wf_context_task_panels ( + context_key varchar2(100) not null + constraint wf_context_panels_context_fk + references wf_contexts(context_key) + on delete cascade, + workflow_key varchar2(100) not null + constraint wf_context_panels_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + transition_key varchar2(100) not null, + sort_key integer not null, + header varchar2(200) not null, + template_url varchar2(500) not null, + /* table constraints */ + constraint wf_context_panels_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade, + constraint wf_context_panels_pk + primary key (context_key, workflow_key, transition_key, sort_key) +); + +create index wf_ctx_panl_workflow_trans_idx on wf_context_task_panels(workflow_key, transition_key); + +comment on table wf_context_task_panels is ' + Holds information about the panels to be displayed on the task page. +'; + + + +create table wf_context_assignments ( + context_key varchar2(100) + constraint wf_context_assign_context_fk + references wf_contexts(context_key) + on delete cascade, + workflow_key varchar2(100) + constraint wf_context_assign_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + transition_key varchar2(100), + party_id integer + constraint wf_context_assign_party_fk + references parties(party_id) + on delete cascade, + /* table constraints */ + constraint wf_context_assign_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade, + constraint wf_context_assign_pk + primary key (context_key, workflow_key, transition_key, party_id) +); + +create index wf_ctx_assg_workflow_trans_idx on wf_context_assignments(workflow_key, transition_key); + +comment on table wf_context_assignments is ' + Static assignment at the context level. +'; + + + +------------------------------------ +-- OPERATIONAL LEVEL OBJECTS +------------------------------------ + +create table wf_cases ( + case_id integer + constraint wf_cases_pk + primary key + constraint wf_cases_acs_object_fk + references acs_objects(object_id) + on delete cascade, + workflow_key varchar2(100) + constraint wf_cases_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + context_key varchar2(100) + constraint wf_cases_context_fk + references wf_contexts(context_key) + on delete cascade, + object_id constraint wf_cases_object_fk + references acs_objects(object_id) + on delete cascade, + -- a toplevel state of the case + state varchar2(40) + default 'created' + constraint wf_cases_state_ck + check (state in ('created', + 'active', + 'suspended', + 'canceled', + 'finished')) +); + +create index wf_cases_workflow_key_idx on wf_cases(workflow_key); +create index wf_cases_context_key_idx on wf_cases(context_key); +create index wf_cases_object_id_idx on wf_cases(object_id); + +comment on table wf_cases is ' + The instance of a workflow, the actual object we''re concerned with in this + workflow. +'; + +comment on column wf_cases.object_id is ' + A case is itself an acs_object, but moreover, a case will always be about some + other acs_object. E.g. for ticket-tracker, the case_id will refer to an instance of + the ticket-tracker-workflow, while the object_id will refer to the ticket itself. + It is possible to have multiple cases around the same object. +'; + + + + +create table wf_case_assignments ( + case_id integer + constraint wf_case_assign_fk + references wf_cases(case_id) + on delete cascade, + workflow_key varchar2(100), + transition_key varchar2(100), + party_id integer + constraint wf_case_assign_party_fk + references parties(party_id) + on delete cascade, + constraint wf_case_assign_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key), + constraint wf_case_assign_pk + primary key (case_id, transition_key, party_id) +); + +create index wf_case_assgn_party_idx on wf_case_assignments(party_id); + +comment on table wf_case_assignments is ' + Manual assignment at the per-case level. +'; + + +create table wf_case_deadlines ( + case_id integer + constraint wf_case_deadline_fk + references wf_cases(case_id) + on delete cascade, + workflow_key varchar2(100), + transition_key varchar2(100), + deadline date + constraint wf_case_deadline_nn + not null, + constraint wf_case_deadline_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key), + constraint wf_case_deadline_pk + primary key (case_id, transition_key) +); + + +comment on table wf_case_deadlines is ' + Manual deadlines for the individual transitions (tasks) on a per-case basis. +'; + + + +create sequence wf_task_id_seq start with 1000 increment by 1; + +create table wf_tasks ( + task_id integer + constraint wf_task_pk + primary key, + case_id integer + constraint wf_task_case_fk + references wf_cases + on delete cascade, + workflow_key varchar2(100) + constraint wf_task_workflow_fk + references wf_workflows(workflow_key), + transition_key varchar2(100), + /* Information about the task */ + state varchar2(40) + default 'enabled' + constraint wf_task_state_ck + check (state in ('enabled','started','canceled', + 'finished','overridden')), + enabled_date date default sysdate, + started_date date, + canceled_date date, + finished_date date, + overridden_date date, + /* -- TIME transition info */ + trigger_time date, + /* -- USER transition info */ + deadline date, + estimated_minutes integer, + holding_user integer + constraint wf_task_holding_user_fk + references users(user_id) + on delete cascade, + hold_timeout date, + /* -- */ + constraint wf_task_transition_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) +); + +create index wf_tasks_case_id_idx on wf_tasks(case_id); + +create index wf_tasks_holding_user_idx on wf_tasks(holding_user); + +comment on table wf_tasks is ' + The tasks that need to be done, who can do it, and what state it''s in. + A task is the instance of a transition. +'; + +create table wf_task_assignments ( + task_id integer + constraint wf_task_assign_task_fk + references wf_tasks(task_id) + on delete cascade, + party_id integer + constraint wf_task_party_fk + references parties(party_id) + on delete cascade, + constraint wf_task_assignments_pk + primary key (task_id, party_id) +); + +create index wf_task_asgn_party_id_idx on wf_task_assignments(party_id); + +create sequence wf_token_id_seq start with 1000 increment by 1; + +create table wf_tokens ( + token_id integer + constraint wf_token_pk + primary key, + case_id integer + constraint wf_token_workflow_instance_fk + references wf_cases(case_id) + on delete cascade, + workflow_key varchar2(100) + constraint wf_token_workflow_fk + references wf_workflows(workflow_key), + -- a token must always be in some place + place_key varchar2(100), + state varchar2(40) default 'free' + constraint wf_tokens_state_ck + check (state in ('free', 'locked', 'canceled', 'consumed')), + -- when the token is locked, by which task + locked_task_id integer + constraint wf_token_task_fk + references wf_tasks(task_id), + -- info on state changes + produced_date date default sysdate, + locked_date date, + canceled_date date, + consumed_date date, + produced_journal_id integer + constraint wf_token_produced_journal_fk + references journal_entries(journal_id), + locked_journal_id integer + constraint wf_token_locked_journal_fk + references journal_entries(journal_id), + canceled_journal_id integer + constraint wf_token_canceled_journal_fk + references journal_entries(journal_id), + consumed_journal_id integer + constraint wf_token_consumed_journal_fk + references journal_entries(journal_id), + constraint wf_token_place_fk + foreign key (workflow_key, place_key) references wf_places(workflow_key, place_key) +); + +create index wf_tokens_case_id_idx on wf_tokens(case_id); + +comment on table wf_tokens is ' + Where the tokens currently are, and what task is laying hands on it, if any. + A token is sort of the instance of a place, except there''ll be one row here per + token, and there can be more than one token per place. +'; + + +/* Should evetually be done by acs_objects automatically */ + +create table wf_attribute_value_audit ( + case_id integer + constraint wf_attr_val_audit_case_fk + references wf_cases(case_id) + on delete cascade, + attribute_id integer + constraint wf_attr_val_audit_attr_fk + references acs_attributes(attribute_id) + on delete cascade, + journal_id integer + constraint wf_attr_val_audit_journal_fk + references journal_entries(journal_id), + attr_value varchar2(4000), + constraint wf_attr_val_audit_pk + primary key (case_id, attribute_id, journal_id) +); + +create index wf_attr_val_aud_attr_id_idx on wf_attribute_value_audit(attribute_id); + +comment on table wf_attribute_value_audit is ' + This table holds all the attribute values that has been set, + so we can track changes over the lifetime of a case. +'; + + +/* + * This is the cartesian product of transitions and contexts. + * We need this in order to compute the following wf_transition_info view, + * because Oracle won't let us outer join against more than one table. + */ +create or replace view wf_transition_contexts as +select t.transition_key, + t.transition_name, + t.workflow_key, + t.sort_order, + t.trigger_type, + c.context_key, + c.context_name +from wf_transitions t, wf_contexts c; + + + +/* + * Returns all the information stored about a certain transition + * in all contexts. You'll usually want to use this with a + * "where context = " clause. + */ +create or replace view wf_transition_info as +select t.transition_key, + t.transition_name, + t.workflow_key, + t.sort_order, + t.trigger_type, + t.context_key, + ct.estimated_minutes, + ct.enable_callback, + ct.enable_custom_arg, + ct.fire_callback, + ct.fire_custom_arg, + ct.assignment_callback, + ct.assignment_custom_arg, + ct.time_callback, + ct.time_custom_arg, + ct.deadline_callback, + ct.deadline_custom_arg, + ct.deadline_attribute_name, + ct.hold_timeout_callback, + ct.hold_timeout_custom_arg, + ct.notification_callback, + ct.notification_custom_arg, + ct.unassigned_callback, + ct.unassigned_custom_arg, + ct.access_privilege +from wf_transition_contexts t, wf_context_transition_info ct +where ct.workflow_key (+) = t.workflow_key + and ct.transition_key (+) = t.transition_key + and ct.context_key (+) = t.context_key; + + + +/* + * This view makes it easy to get the input/output places of a transition + */ +create or replace view wf_transition_places as +select a.workflow_key, + t.transition_key, + p.place_key, + p.place_name, + p.sort_order, + a.direction, + a.guard_callback, + a.guard_custom_arg, + a.guard_description +from wf_arcs a, wf_places p, wf_transitions t +where a.transition_key = t.transition_key +and a.workflow_key = t.workflow_key +and p.place_key = a.place_key +and p.workflow_key = a.workflow_key; + + +/* + * This view returns information about all currently enabled transitions. + * It does not include transitions that are started. This information, along + * with additional, dynamic information, such as the user assignment or the + * time a timed transition triggers, is then stored in wf_tasks. + * + * Contrary to wf_tasks, this is authoritative, in that it queries + * the actual state of the workflow net. + * + * The logic behind this view is: All transitions in all cases, for which + * there does not exists a place for which there is not a free token. + */ +create or replace view wf_enabled_transitions as +select c.case_id, + t.transition_key, + t.transition_name, + t.workflow_key, + t.sort_order, + t.trigger_type, + t.context_key, + t.enable_callback, + t.enable_custom_arg, + t.fire_callback, + t.fire_custom_arg, + t.assignment_callback, + t.assignment_custom_arg, + t.time_callback, + t.time_custom_arg, + t.deadline_callback, + t.deadline_custom_arg, + t.deadline_attribute_name, + t.hold_timeout_callback, + t.hold_timeout_custom_arg, + t.notification_callback, + t.notification_custom_arg, + t.unassigned_callback, + t.unassigned_custom_arg, + t.estimated_minutes, + t.access_privilege + from wf_transition_info t, + wf_cases c + where t.workflow_key = c.workflow_key + and t.context_key = c.context_key + and c.state = 'active' + and not exists + (select tp.place_key + from wf_transition_places tp + where tp.transition_key = t.transition_key + and tp.workflow_key = t.workflow_key + and tp.direction = 'in' + and not exists + (select tk.token_id + from wf_tokens tk + where tk.place_key = tp.place_key + and tk.case_id = c.case_id + and tk.state = 'free' + ) + ); + + + + + +/* + * This view joins wf_tasks with the parties data model to figure out who can perform the tasks. + * It should contain one row per ( user x task ) + */ + +/* Replaced 'unique' with 'distinct', because Stas had problems with Oracle behaving mysteriously */ + +create or replace view wf_user_tasks as +select distinct ta.task_id, + ta.case_id, + ta.workflow_key, + ta.transition_key, + tr.transition_name, + ta.enabled_date, + ta.started_date, + u.user_id, + ta.state, + ta.holding_user, + ta.hold_timeout, + ta.deadline, + ta.estimated_minutes +from wf_tasks ta, + wf_task_assignments tasgn, + wf_cases c, + wf_transition_info tr, + party_approved_member_map m, + users u +where ta.state in ( 'enabled','started') +and c.case_id = ta.case_id +and c.state = 'active' +and tr.transition_key = ta.transition_key +and tr.trigger_type = 'user' +and tr.context_key = c.context_key +and tasgn.task_id = ta.task_id +and m.party_id = tasgn.party_id +and u.user_id = m.member_id; + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-core-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-core-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/wf-core-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,76 @@ +-- +-- acs-workflow/sql/wf-core-drop.sql +-- +-- Drops the data model and views for the workflow package. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: wf-core-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +/* Drop all cases and all workflows */ +begin + for workflow_rec in (select w.workflow_key, t.table_name + from wf_workflows w, acs_object_types t + where t.object_type = w.workflow_key) + loop + workflow.delete_cases(workflow_key => workflow_rec.workflow_key); + + execute immediate 'drop table '||workflow_rec.table_name; + workflow.drop_workflow(workflow_key => workflow_rec.workflow_key); + end loop; +end; +/ +show errors; + + +/* Sequences */ +drop sequence wf_task_id_seq; +drop sequence wf_token_id_seq; + +/* Views */ + +drop view wf_user_tasks; +drop view wf_enabled_transitions; +drop view wf_transition_places; +drop view wf_transition_info; +drop view wf_transition_contexts; + +/* Operational level */ +drop table wf_attribute_value_audit; +drop table wf_tokens; +drop table wf_task_assignments; +drop table wf_tasks; +drop table wf_case_assignments; +drop table wf_case_deadlines; +drop table wf_cases; + +/* Context level */ +drop table wf_context_assignments; +drop table wf_context_task_panels; +drop table wf_context_transition_info; +drop table wf_contexts; + +/* Knowledge Level */ +drop table wf_attribute_info; +drop table wf_transition_assignment_map; +drop table wf_transition_attribute_map; +drop table wf_arcs; +drop table wf_transitions; +drop table wf_places; +drop table wf_workflows; + +/* acs_object_type */ + +begin + acs_object_type.drop_type( + object_type => 'workflow', + cascade_p => 't' + ); +end; +/ +show errors + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/workflow-case-package.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/workflow-case-package.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/workflow-case-package.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,1952 @@ +-- +-- acs-workflow/sql/workflow-case-package.sql +-- +-- Creates the PL/SQL package that provides the API for interacting +-- with a workflow case. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: workflow-case-package.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + + +create or replace package workflow_case +is + + function new ( + case_id in number default null, + workflow_key in varchar2, + context_key in varchar2 default null, + object_id in integer, + creation_date in date default sysdate, + creation_user in integer default null, + creation_ip in varchar2 default null + ) return integer; + + procedure add_manual_assignment ( + case_id in number, + transition_key in varchar2, + party_id in number + ); + + procedure remove_manual_assignment ( + case_id in number, + transition_key in varchar2, + party_id in number + ); + + procedure clear_manual_assignments ( + case_id in number, + transition_key in varchar2 + ); + + procedure start_case ( + case_id in number, + creation_user in integer default null, + creation_ip in varchar2 default null, + msg in varchar2 default null + ); + + procedure delete( + case_id in number + ); + + procedure suspend( + case_id in number, + user_id in number default null, + ip_address in varchar2 default null, + msg in varchar2 default null + ); + + procedure resume( + case_id in number, + user_id in number default null, + ip_address in varchar2 default null, + msg in varchar2 default null + ); + + procedure cancel( + case_id in number, + user_id in number default null, + ip_address in varchar2 default null, + msg in varchar2 default null + ); + + procedure fire_message_transition ( + task_id in number + ); + + /* To perform an action on the workflow: + * (numbers in parenthesis is the number of times each function should get called) + * + * 1. begin_task_action (1) + * 2. set_attribute_value (0..*) + * 3. clear_manual_assignments (0..1) + * 4. add_manual_assignment (0..*) + * 5. end_task_action (1) + */ + function begin_task_action ( + task_id in number, + action in varchar2, + action_ip in varchar2, + user_id in number, + msg in varchar2 default null + ) return number; + + procedure set_attribute_value ( + journal_id in number, + attribute_name in varchar2, + value in varchar2 + ); + + procedure end_task_action ( + journal_id in number, + action in varchar2, + task_id in number + ); + + /* Shortcut, that does both begin and end, when you have no attributes to set or assignments to make */ + function task_action ( + task_id in number, + action in varchar2, + action_ip in varchar2, + user_id in number, + msg in varchar2 default null + ) return number; + + function get_attribute_value ( + case_id in number, + attribute_name in varchar2 + ) return varchar2; + + procedure add_task_assignment ( + task_id in number, + party_id in number + ); + + procedure remove_task_assignment ( + task_id in number, + party_id in number + ); + + procedure clear_task_assignments ( + task_id in number + ); + + + /* DBMS_JOBS */ + + procedure sweep_timed_transitions; + + procedure sweep_hold_timeout; + +end workflow_case; +/ +show errors; + +create or replace package body workflow_case +is + + /* + * FORWARD DECLARATIONS + */ + + procedure add_token ( + case_id in number, + place_key in varchar2, + journal_id in number + ); + + procedure lock_token ( + case_id in number, + place_key in varchar2, + journal_id in number, + task_id in number + ); + + procedure release_token ( + task_id in number, + journal_id in number + ); + + procedure consume_token ( + case_id in number, + place_key in varchar2, + journal_id in number, + task_id in number default null + ); + + procedure execute_unassigned_callback ( + callback in varchar2, + task_id in number, + custom_arg in varchar2 + ); + + procedure enable_transitions ( + case_id in number + ); + + procedure sweep_automatic_transitions ( + case_id in number, + journal_id in number + ); + + function finished_p( + case_id in number, + journal_id in number + ) return char; + + procedure fire_transition_internal ( + task_id in number, + journal_id in number + ); + + procedure start_task( + task_id in number, + user_id in number, + journal_id in number + ); + + procedure cancel_task( + task_id in number, + journal_id in number + ); + + procedure finish_task ( + task_id in number, + journal_id in number + ); + + procedure notify_assignee( + task_id in wf_tasks.task_id%TYPE, + user_id in users.user_id%TYPE, + callback in wf_context_transition_info.notification_callback%TYPE, + custom_arg in wf_context_transition_info.notification_custom_arg%TYPE + ); + + /* + * CURSORS + */ + + cursor input_places( + workflow_key in wf_workflows.workflow_key%TYPE, + transition_key in wf_transitions.transition_key%TYPE + ) + return wf_transition_places%ROWTYPE + is + select * + from wf_transition_places tp + where tp.workflow_key = input_places.workflow_key + and tp.transition_key = input_places.transition_key + and direction = 'in'; + + + cursor output_places ( + workflow_key in wf_workflows.workflow_key%TYPE, + transition_key in wf_transitions.transition_key%TYPE + ) + return wf_transition_places%ROWTYPE + is + select * + from wf_transition_places tp + where tp.workflow_key = output_places.workflow_key + and tp.transition_key = output_places.transition_key + and direction = 'out'; + + + /* + * PUBLIC API + */ + + function new ( + case_id in number default null, + workflow_key in varchar2, + context_key in varchar2 default null, + object_id in integer, + creation_date in date default sysdate, + creation_user in integer default null, + creation_ip in varchar2 default null + ) + return integer + is + v_case_id number; + v_workflow_case_table varchar2(30); + v_context_key_for_query varchar2(100); + begin + if context_key is null then + v_context_key_for_query := 'default'; + else + v_context_key_for_query := context_key; + end if; + + /* insert a row into acs_objects */ + v_case_id := acs_object.new( + object_id => new.case_id, + object_type => new.workflow_key, + creation_date => new.creation_date, + creation_user => new.creation_user, + creation_ip => new.creation_ip + ); + + /* insert the case in to the general wf_cases table */ + insert into wf_cases + (case_id, workflow_key, context_key, object_id, state) + values + (v_case_id, new.workflow_key, v_context_key_for_query, new.object_id, 'created'); + + /* insert the case into the workflow-specific cases table */ + select table_name into v_workflow_case_table + from acs_object_types + where object_type = new.workflow_key; + + execute immediate 'insert into '||v_workflow_case_table||' (case_id) values (:1)' + using v_case_id; + + return v_case_id; + end new; + + + procedure add_manual_assignment ( + case_id in number, + transition_key in varchar2, + party_id in number + ) + is + v_workflow_key varchar2(100); + begin + select workflow_key into v_workflow_key from wf_cases where case_id = add_manual_assignment.case_id; + + insert into wf_case_assignments + (case_id, workflow_key, transition_key, party_id) + values + (add_manual_assignment.case_id, v_workflow_key, add_manual_assignment.transition_key, add_manual_assignment.party_id); + end add_manual_assignment; + + + procedure remove_manual_assignment ( + case_id in number, + transition_key in varchar2, + party_id in number + ) + is + v_workflow_key varchar2(100); + begin + select workflow_key into v_workflow_key from wf_cases where case_id = remove_manual_assignment.case_id; + + delete from wf_case_assignments + where workflow_key = v_workflow_key + and case_id = remove_manual_assignment.case_id + and transition_key = remove_manual_assignment.transition_key + and party_id = remove_manual_assignment.party_id; + end remove_manual_assignment; + + procedure clear_manual_assignments ( + case_id in number, + transition_key in varchar2 + ) + is + v_workflow_key varchar2(100); + begin + select workflow_key into v_workflow_key from wf_cases where case_id = clear_manual_assignments.case_id; + + delete + from wf_case_assignments + where workflow_key = v_workflow_key + and case_id = clear_manual_assignments.case_id + and transition_key = clear_manual_assignments.transition_key; + end clear_manual_assignments; + + + procedure start_case ( + case_id in number, + creation_user in integer default null, + creation_ip in varchar2 default null, + msg in varchar2 default null + ) + is + v_journal_id number; + begin + /* Add an entry to the journal */ + v_journal_id := journal_entry.new( + object_id => start_case.case_id, + action => 'case start', + action_pretty => 'Case started', + creation_user => start_case.creation_user, + creation_ip => start_case.creation_ip, + msg => start_case.msg + ); + + update wf_cases set state = 'active' where case_id = start_case.case_id; + + add_token( + case_id => start_case.case_id, + place_key => 'start', + journal_id => v_journal_id + ); + + /* Turn the wheels */ + sweep_automatic_transitions( + case_id => start_case.case_id, + journal_id => v_journal_id + ); + end start_case; + + + procedure delete( + case_id in number + ) + is + v_workflow_case_table varchar2(30); + begin + /* delete attribute_value_audit, tokens, tasks */ + delete from wf_attribute_value_audit where case_id = workflow_case.delete.case_id; + delete from wf_case_assignments where case_id = workflow_case.delete.case_id; + delete from wf_case_deadlines where case_id = workflow_case.delete.case_id; + delete from wf_tokens where case_id = workflow_case.delete.case_id; + delete from wf_task_assignments where task_id in (select task_id from wf_tasks where case_id = workflow_case.delete.case_id); + delete from wf_tasks where case_id = workflow_case.delete.case_id; + + /* delete the journal */ + journal_entry.delete_for_object(workflow_case.delete.case_id); + + /* delete from the workflow-specific cases table */ + select table_name into v_workflow_case_table + from acs_object_types ot, wf_cases c + where c.case_id = workflow_case.delete.case_id + and object_type = c.workflow_key; + + execute immediate 'delete from '||v_workflow_case_table||' where case_id = :case_id' + using in workflow_case.delete.case_id; + + /* delete from the generic cases table */ + delete from wf_cases where case_id = workflow_case.delete.case_id; + + /* delete from acs-objects */ + acs_object.delete(workflow_case.delete.case_id); + end delete; + + + procedure suspend( + case_id in number, + user_id in number default null, + ip_address in varchar2 default null, + msg in varchar2 default null + ) + is + v_state varchar2(40); + v_journal_id number; + begin + select state into v_state + from wf_cases + where case_id = suspend.case_id; + + if v_state != 'active' then + raise_application_error(-20000, 'Only active cases can be suspended'); + end if; + + /* Add an entry to the journal */ + v_journal_id := journal_entry.new( + object_id => suspend.case_id, + action => 'case suspend', + action_pretty => 'case suspended', + creation_user => suspend.user_id, + creation_ip => suspend.ip_address, + msg => suspend.msg + ); + + update wf_cases + set state = 'suspended' + where case_id = suspend.case_id; + end suspend; + + + procedure resume( + case_id in number, + user_id in number default null, + ip_address in varchar2 default null, + msg in varchar2 default null + ) + is + v_state varchar2(40); + v_journal_id number; + begin + select state into v_state + from wf_cases + where case_id = resume.case_id; + + if v_state != 'suspended' and v_state != 'canceled' then + raise_application_error(-20000, 'Only suspended or canceled cases can be resumed'); + end if; + + /* Add an entry to the journal */ + v_journal_id := journal_entry.new( + object_id => resume.case_id, + action => 'case resume', + action_pretty => 'case resumed', + creation_user => resume.user_id, + creation_ip => resume.ip_address, + msg => resume.msg + ); + + update wf_cases + set state = 'active' + where case_id = resume.case_id; + end resume; + + + procedure cancel( + case_id in number, + user_id in number default null, + ip_address in varchar2 default null, + msg in varchar2 default null + ) + is + v_state varchar2(40); + v_journal_id number; + begin + select state into v_state + from wf_cases + where case_id = cancel.case_id; + + if v_state != 'active' and v_state != 'suspended' then + raise_application_error(-20000, 'Only active or suspended cases can be canceled'); + end if; + + /* Add an entry to the journal */ + v_journal_id := journal_entry.new( + object_id => cancel.case_id, + action => 'case cancel', + action_pretty => 'Case canceled', + creation_user => cancel.user_id, + creation_ip => cancel.ip_address, + msg => cancel.msg + ); + + update wf_cases + set state = 'canceled' + where case_id = cancel.case_id; + end cancel; + + + procedure fire_message_transition ( + task_id in number + ) is + v_case_id number; + v_transition_name varchar2(100); + v_trigger_type varchar2(40); + v_journal_id number; + begin + select t.case_id, tr.transition_name, tr.trigger_type + into v_case_id, v_transition_name, v_trigger_type + from wf_tasks t, wf_transitions tr + where t.task_id = fire_message_transition.task_id + and tr.workflow_key = t.workflow_key + and tr.transition_key = t.transition_key; + + if v_trigger_type != 'message' then + raise_application_error(-20000, 'Transition '''||v_transition_name||''' is not message triggered'); + end if; + + /* Add an entry to the journal */ + v_journal_id := journal_entry.new( + object_id => v_case_id, + action => 'task '||fire_message_transition.task_id||' fire', + action_pretty => v_transition_name || ' fired' + ); + + fire_transition_internal( + task_id => fire_message_transition.task_id, + journal_id => v_journal_id + ); + + sweep_automatic_transitions( + case_id => v_case_id, + journal_id => v_journal_id + ); + end fire_message_transition; + + + /* + * A wrapper for user tasks that uses the start/commit/cancel model for firing transitions. + */ + function begin_task_action ( + task_id in number, + action in varchar2, + action_ip in varchar2, + user_id in number, + msg in varchar2 default null + ) + return number + is + v_state varchar2(40); + v_journal_id number; + v_case_id number; + v_transition_name varchar2(100); + v_num_rows number; + begin + select state into v_state + from wf_tasks + where task_id = begin_task_action.task_id; + + if begin_task_action.action = 'start' then + if v_state != 'enabled' then + raise_application_error(-20000, 'Task is in state '''||v_state||''', '|| + 'but it must be in state ''enabled'' to be started.'); + end if; + + select decode(count(*),0,0,1) into v_num_rows + from wf_user_tasks + where task_id = begin_task_action.task_id + and user_id = begin_task_action.user_id; + + if v_num_rows = 0 then + raise_application_error(-20000, 'You are not assigned to this task.'); + end if; + elsif begin_task_action.action = 'finish' or begin_task_action.action = 'cancel' then + + if v_state = 'started' then + /* Is this user the holding user? */ + select decode(count(*),0,0,1) into v_num_rows + from wf_tasks + where task_id = begin_task_action.task_id + and holding_user = begin_task_action.user_id; + if v_num_rows = 0 then + raise_application_error(-20000, 'You are the user currently working on this task.'); + end if; + elsif v_state = 'enabled' then + if begin_task_action.action = 'cancel' then + raise_application_error(-20000, 'You can only cancel a task in state ''started'', '|| + 'but this task is in state '''||v_state||''''); + end if; + + /* Is this user assigned to this task? */ + select decode(count(*),0,0,1) into v_num_rows + from wf_user_tasks + where task_id = begin_task_action.task_id + and user_id = begin_task_action.user_id; + if v_num_rows = 0 then + raise_application_error(-20000, 'You are not assigned to this task.'); + end if; + + /* This task is finished without an explicit start. + * Store the user as the holding_user */ + update wf_tasks + set holding_user = begin_task_action.user_id + where task_id = begin_task_action.task_id; + else + raise_application_error(-20000, 'Task is in state '''||v_state||''', '|| + 'but it must be in state ''enabled'' or ''started'' to be finished'); + end if; + + elsif begin_task_action.action = 'comment' then + -- We currently allow anyone to comment on a task + -- (need this line because PL/SQL doens't like empty if blocks) + v_num_rows := 0; + end if; + + select t.case_id, tr.transition_name into v_case_id, v_transition_name + from wf_tasks t, + wf_transitions tr + where t.task_id = begin_task_action.task_id + and tr.workflow_key = t.workflow_key + and tr.transition_key = t.transition_key; + + /* Insert a journal entry */ + + v_journal_id := journal_entry.new( + object_id => v_case_id, + action => 'task '||begin_task_action.task_id||' '||begin_task_action.action, + action_pretty => v_transition_name || ' ' || begin_task_action.action, + creation_user => begin_task_action.user_id, + creation_ip => begin_task_action.action_ip, + msg => begin_task_action.msg + ); + + return v_journal_id; + end begin_task_action; + + + procedure end_task_action ( + journal_id in number, + action in varchar2, + task_id in number + ) + is + v_user_id number; + begin + select creation_user into v_user_id + from acs_objects + where object_id = end_task_action.journal_id; + + /* Update the workflow state */ + + if end_task_action.action = 'start' then + start_task(end_task_action.task_id, v_user_id, end_task_action.journal_id); + elsif end_task_action.action = 'finish' then + finish_task(end_task_action.task_id, end_task_action.journal_id); + elsif end_task_action.action = 'cancel' then + cancel_task(end_task_action.task_id, end_task_action.journal_id); + elsif end_task_action.action != 'comment' then + raise_application_error(-20000, 'Unknown action ''' || end_task_action.action || ''''); + end if; + + end end_task_action; + + function task_action ( + task_id in number, + action in varchar2, + action_ip in varchar2, + user_id in number, + msg in varchar2 default null + ) return number + is + v_journal_id integer; + begin + v_journal_id := begin_task_action( + task_id => task_action.task_id, + action => task_action.action, + action_ip => task_action.action_ip, + user_id => task_action.user_id, + msg => task_action.msg + ); + + end_task_action( + journal_id => v_journal_id, + action => task_action.action, + task_id => task_action.task_id + ); + + return v_journal_id; + end task_action; + + + procedure set_attribute_value ( + journal_id in number, + attribute_name in varchar2, + value in varchar2 + ) + is + v_workflow_key varchar2(100); + v_case_id number; + v_attribute_id number; + begin + select o.object_type, o.object_id into v_workflow_key, v_case_id + from journal_entries je, acs_objects o + where je.journal_id = set_attribute_value.journal_id + and o.object_id = je.object_id; + + select attribute_id into v_attribute_id + from acs_attributes + where object_type = v_workflow_key + and attribute_name = set_attribute_value.attribute_name; + + acs_object.set_attribute( + object_id_in => v_case_id, + attribute_name_in => set_attribute_value.attribute_name, + value_in => set_attribute_value.value + ); + + insert into wf_attribute_value_audit + (case_id, attribute_id, journal_id, attr_value) + values + (v_case_id, v_attribute_id, set_attribute_value.journal_id, set_attribute_value.value); + end set_attribute_value; + + + function get_attribute_value ( + case_id in number, + attribute_name in varchar2 + ) + return varchar2 + is + begin + return acs_object.get_attribute( + object_id_in => get_attribute_value.case_id, + attribute_name_in => get_attribute_value.attribute_name + ); + end get_attribute_value; + + + procedure add_task_assignment ( + task_id in number, + party_id in number + ) + is + v_count integer; + v_workflow_key wf_workflows.workflow_key%TYPE; + v_context_key wf_contexts.context_key%TYPE; + v_case_id wf_cases.case_id%TYPE; + v_transition_key wf_transitions.transition_key%TYPE; + v_notification_callback wf_context_transition_info.notification_callback%TYPE; + v_notification_custom_arg wf_context_transition_info.notification_custom_arg%TYPE; + + -- might need to tune this query further + cursor c_new_assigned_users is + select distinct u.user_id + from users u + where u.user_id not in ( + select distinct u2.user_id + from wf_task_assignments tasgn2, + party_approved_member_map m2, + users u2 + where tasgn2.task_id = add_task_assignment.task_id + and m2.party_id = tasgn2.party_id + and u2.user_id = m2.member_id) + and exists ( + select 1 + from party_approved_member_map m + where m.member_id = u.user_id + and m.party_id = add_task_assignment.party_id + ); + cursor c_callback is + select notification_callback, + notification_custom_arg + from wf_context_transition_info + where context_key = v_context_key + and workflow_key = v_workflow_key + and transition_key = v_transition_key; + callback_rec c_callback%ROWTYPE; + + begin + -- check that we don't hit the unique constraint + + select count(*) into v_count + from wf_task_assignments + where task_id = add_task_assignment.task_id + and party_id = add_task_assignment.party_id; + + if v_count > 0 then + return; + end if; + + -- get the notification callback info + + select case_id, workflow_key, transition_key + into v_case_id, v_workflow_key, v_transition_key + from wf_tasks + where task_id = add_task_assignment.task_id; + + select context_key + into v_context_key + from wf_cases + where case_id = v_case_id; + + open c_callback; + fetch c_callback into callback_rec; + + if c_callback%FOUND then + v_notification_callback := callback_rec.notification_callback; + v_notification_custom_arg := callback_rec.notification_custom_arg; + else + v_notification_callback := null; + v_notification_custom_arg := null; + end if; + + -- notify any new assignees + + for v_assigned_user in c_new_assigned_users loop + notify_assignee( + task_id => add_task_assignment.task_id, + user_id => v_assigned_user.user_id, + callback => v_notification_callback, + custom_arg => v_notification_custom_arg + ); + end loop; + + -- do the insert + + insert into wf_task_assignments + (task_id, party_id) + values + (add_task_assignment.task_id, add_task_assignment.party_id); + + + end add_task_assignment; + + procedure remove_task_assignment ( + task_id in number, + party_id in number + ) + is + v_num_assigned number; + v_case_id number; + v_workflow_key varchar2(100); + v_transition_key varchar2(100); + v_context_key varchar2(100); + + cursor c_callback + is + select unassigned_callback, unassigned_custom_arg + from wf_context_transition_info + where workflow_key = v_workflow_key + and context_key = v_context_key + and transition_key = v_transition_key; + callback_rec c_callback%ROWTYPE; + begin + delete + from wf_task_assignments + where task_id = remove_task_assignment.task_id + and party_id = remove_task_assignment.party_id; + + select count(*) into v_num_assigned + from wf_task_assignments + where task_id = remove_task_assignment.task_id; + + if v_num_assigned > 0 then + return; + end if; + + select case_id, transition_key + into v_case_id, v_transition_key + from wf_tasks + where task_id = remove_task_assignment.task_id; + + select workflow_key, context_key + into v_workflow_key, v_context_key + from wf_cases + where case_id = v_case_id; + + open c_callback; + fetch c_callback into callback_rec; + + if c_callback%FOUND then + execute_unassigned_callback ( + callback => callback_rec.unassigned_callback, + task_id => task_id, + custom_arg => callback_rec.unassigned_custom_arg + ); + end if; + close c_callback; + + end remove_task_assignment; + + procedure clear_task_assignments ( + task_id in number + ) + is + v_case_id number; + v_transition_key varchar2(100); + v_workflow_key varchar2(100); + v_context_key varchar2(100); + v_callback varchar2(100); + v_custom_arg varchar2(4000); + begin + delete + from wf_task_assignments + where task_id = clear_task_assignments.task_id; + + select case_id, transition_key + into v_case_id, v_transition_key + from wf_tasks + where task_id = clear_task_assignments.task_id; + + select workflow_key, context_key + into v_workflow_key, v_context_key + from wf_cases + where case_id = v_case_id; + + select unassigned_callback, unassigned_custom_arg + into v_callback, v_custom_arg + from wf_context_transition_info + where workflow_key = v_workflow_key + and context_key = v_context_key + and transition_key = v_transition_key; + + execute_unassigned_callback ( + callback => v_callback, + task_id => task_id, + custom_arg => v_custom_arg + ); + + end clear_task_assignments; + + /* + * PRIVATE + */ + + function evaluate_guard ( + callback in varchar2, + custom_arg in varchar2, + case_id in number, + workflow_key in varchar2, + transition_key in varchar2, + place_key in varchar2, + direction in varchar2 + ) + return char + is + v_guard_happy_p char(1); + begin + if callback is null then + -- null guard evaluates to true + return 't'; + else + if callback = '#' then + return 'f'; + else + execute immediate 'begin :1 := ' || callback + || '(:2, :3, :4, :5, :6, :7); end;' + using out v_guard_happy_p, + in case_id, + in workflow_key, + in transition_key, + in place_key, + in direction, + in custom_arg; + return v_guard_happy_p; + end if; + end if; + end evaluate_guard; + + + procedure execute_transition_callback( + callback in varchar2, + custom_arg in varchar2, + case_id in number, + transition_key in varchar2 + ) + is + begin + if callback is not null then + execute immediate 'begin '||callback + || '(:1, :2, :3); end;' + using in case_id, + in transition_key, + in custom_arg; + end if; + end execute_transition_callback; + + + function execute_time_callback ( + callback in varchar2, + custom_arg in varchar2, + case_id in number, + transition_key in varchar2 + ) + return date + is + v_trigger_time date; + begin + if callback is null then + raise_application_error(-20000, 'There''s no time_callback function for the timed transition ''' || transition_key || ''''); + end if; + + execute immediate 'begin :1 := ' || callback + || '(:2, :3, :4); end;' + using out v_trigger_time, + in case_id, + in transition_key, + in custom_arg; + + return v_trigger_time; + end execute_time_callback; + + + function get_task_deadline ( + callback in varchar2, + custom_arg in varchar2, + attribute_name in varchar2, + case_id in number, + transition_key in varchar2 + ) + return date + is + cursor case_deadline_cur is + select deadline + from wf_case_deadlines + where case_id = get_task_deadline.case_id + and transition_key = get_task_deadline.transition_key; + v_deadline date; + begin + /* + * 1. or if there's a row in wf_case_deadlines, we use that + * 2. if there is a callback, we execute that + * 3. otherwise, if there is an attribute, we use that + */ + + /* wf_case_deadlines */ + open case_deadline_cur; + fetch case_deadline_cur into v_deadline; + if case_deadline_cur%NOTFOUND then + if callback is not null then + /* callback */ + execute immediate 'begin :1 := ' || callback + || '(:2, :3, :4); end;' + using out v_deadline, + in case_id, + in transition_key, + in custom_arg; + elsif attribute_name is not null then + /* attribute */ + v_deadline := acs_object.get_attribute( + object_id_in => get_task_deadline.case_id, + attribute_name_in => get_task_deadline.attribute_name + ); + else + v_deadline := null; + end if; + end if; + + return v_deadline; + end get_task_deadline; + + + function execute_hold_timeout_callback ( + callback in varchar2, + custom_arg in varchar2, + case_id in number, + transition_key in varchar2 + ) + return date + is + v_hold_timeout date; + begin + if callback is null then + return null; + end if; + + execute immediate 'begin :1 := ' || callback + || '(:2, :3, :4); end;' + using out v_hold_timeout, + in case_id, + in transition_key, + in custom_arg; + + return v_hold_timeout; + end execute_hold_timeout_callback; + + + procedure execute_unassigned_callback ( + callback in varchar2, + task_id in number, + custom_arg in varchar2 + ) + is + begin + if callback is not null then + execute immediate 'begin ' || callback + || '(:1, :2); end;' + using in task_id, + in custom_arg; + end if; + end execute_unassigned_callback; + + procedure set_task_assignments( + task_id in number, + callback in varchar2, + custom_arg in varchar2 + ) + is + cursor case_assignments is + select party_id + from wf_case_assignments ca, wf_tasks t + where t.task_id = set_task_assignments.task_id + and ca.case_id = t.case_id + and ca.transition_key = t.transition_key; + cursor context_assignments is + select party_id + from wf_context_assignments ca, wf_cases c, wf_tasks t + where t.task_id = set_task_assignments.task_id + and c.case_id = t.case_id + and ca.context_key = c.context_key + and ca.workflow_key = t.workflow_key + and ca.transition_key = t.transition_key; + v_done_p char(1); + begin + + /* Find out who to assign the given task to. + * + * 1. See if there are rows in wf_case_assignments. + * 2. If not, and a callback is defined, execute that. + * 3. Otherwise, grab the assignment from the workflow context. + * + * (We used to use the callback first, but that makes + * reassignment of tasks difficult.) + */ + + v_done_p := 'f'; + for case_assignment_rec in case_assignments loop + v_done_p := 't'; + add_task_assignment ( + task_id => task_id, + party_id => case_assignment_rec.party_id + ); + end loop; + + if v_done_p != 't' then + + if callback is not null then + execute immediate 'begin '|| set_task_assignments.callback + || '(:1, :2); end;' + using in set_task_assignments.task_id, + in set_task_assignments.custom_arg; + else + for context_assignment_rec in context_assignments loop + add_task_assignment ( + task_id => task_id, + party_id => context_assignment_rec.party_id + ); + end loop; + end if; + end if; + end set_task_assignments; + + + procedure add_token ( + case_id in number, + place_key in varchar2, + journal_id in number + ) + is + v_token_id number; + v_workflow_key varchar2(100); + begin + select wf_token_id_seq.nextval into v_token_id from dual; + + select workflow_key into v_workflow_key + from wf_cases c + where c.case_id = add_token.case_id; + + insert into wf_tokens + (token_id, case_id, workflow_key, place_key, state, produced_journal_id) + values + (v_token_id, add_token.case_id, v_workflow_key, add_token.place_key, 'free', add_token.journal_id); + end add_token; + + + procedure lock_token ( + case_id in number, + place_key in varchar2, + journal_id in number, + task_id in number + ) + is + begin + update wf_tokens + set state = 'locked', + locked_task_id = lock_token.task_id, + locked_date = sysdate, + locked_journal_id = lock_token.journal_id + where case_id = lock_token.case_id + and place_key = lock_token.place_key + and state = 'free' + and rownum = 1; + end lock_token; + + + procedure release_token ( + task_id in number, + journal_id in number + ) + is + cursor token_cur is + select token_id, + case_id, + place_key + from wf_tokens + where state = 'locked' + and locked_task_id = release_token.task_id; + begin + /* Add a new token for each released one */ + for token_rec in token_cur loop + add_token( + case_id => token_rec.case_id, + place_key => token_rec.place_key, + journal_id => release_token.journal_id + ); + end loop; + + /* Mark the released ones canceled */ + update wf_tokens + set state = 'canceled', + canceled_date = sysdate, + canceled_journal_id = release_token.journal_id + where state = 'locked' + and locked_task_id = release_token.task_id; + end release_token; + + + procedure consume_token ( + case_id in number, + place_key in varchar2, + journal_id in number, + task_id in number default null + ) + is + begin + if task_id is null then + update wf_tokens + set state = 'consumed', + consumed_date = sysdate, + consumed_journal_id = consume_token.journal_id + where case_id = consume_token.case_id + and place_key = consume_token.place_key + and state = 'free' + and rownum = 1; + else + update wf_tokens + set state = 'consumed', + consumed_date = sysdate, + consumed_journal_id = consume_token.journal_id + where case_id = consume_token.case_id + and place_key = consume_token.place_key + and state = 'locked' + and locked_task_id = consume_token.task_id; + end if; + end consume_token; + + + procedure sweep_automatic_transitions ( + case_id in number, + journal_id in number + ) is + cursor enabled_automatic_transitions is + select task_id + from wf_tasks ta, wf_transitions tr + where tr.workflow_key = ta.workflow_key + and tr.transition_key = ta.transition_key + and tr.trigger_type = 'automatic' + and ta.state = 'enabled' + and ta.case_id = sweep_automatic_transitions.case_id; + v_done_p char(1); + v_finished_p char(1); + begin + + enable_transitions(case_id => sweep_automatic_transitions.case_id); + + loop + v_done_p := 't'; + v_finished_p := finished_p( + case_id => sweep_automatic_transitions.case_id, + journal_id => sweep_automatic_transitions.journal_id); + + if v_finished_p = 'f' then + for task_rec in enabled_automatic_transitions loop + fire_transition_internal( + task_id => task_rec.task_id, + journal_id => sweep_automatic_transitions.journal_id + ); + v_done_p := 'f'; + end loop; + enable_transitions(case_id => sweep_automatic_transitions.case_id); + end if; + + exit when v_done_p = 't'; + end loop; + end sweep_automatic_transitions; + + + function finished_p ( + case_id in number, + journal_id in number + ) + return char + is + v_case_state varchar2(40); + v_token_id number; + v_num_rows number; + v_journal_id number; + begin + select state into v_case_state + from wf_cases + where case_id = finished_p.case_id; + + if v_case_state = 'finished' then + return 't'; + else + /* Let's see if the case is actually finished, but just not marked so */ + select decode(count(*),0,0,1) into v_num_rows + from wf_tokens + where case_id = finished_p.case_id + and place_key = 'end'; + + if v_num_rows = 0 then + return 'f'; + else + /* There's a token in the end place. + * Count the total number of tokens to make sure the wf is well-constructed. + */ + + select decode(count(*),0,0,1,1,2) into v_num_rows + from wf_tokens + where case_id = finished_p.case_id + and state in ('free', 'locked'); + + if v_num_rows > 1 then + raise_application_error(-20000, 'The workflow net is misconstructed: Some parallel executions have not finished.'); + end if; + + /* Consume that token */ + select token_id into v_token_id + from wf_tokens + where case_id = finished_p.case_id + and state in ('free', 'locked'); + + consume_token( + case_id => finished_p.case_id, + place_key => 'end', + journal_id => finished_p.journal_id + ); + + update wf_cases + set state = 'finished' + where case_id = finished_p.case_id; + + /* Add an extra entry to the journal */ + v_journal_id := journal_entry.new( + object_id => finished_p.case_id, + action => 'case finish', + action_pretty => 'Case finished' + ); + + return 't'; + end if; + end if; + end finished_p; + + + + /* This procedure should be scheduled to run as a dbms_job. */ + procedure sweep_timed_transitions + is + cursor timed_transitions_to_fire is + select t.task_id, t.case_id, tr.transition_name + from wf_tasks t, wf_transitions tr + where trigger_time <= sysdate + and state = 'enabled' + and tr.workflow_key = t.workflow_key + and tr.transition_key = t.transition_key; + v_journal_id number; + begin + for trans_rec in timed_transitions_to_fire loop + + /* Insert an entry to the journal so people will know it fired */ + + v_journal_id := journal_entry.new( + object_id => trans_rec.case_id, + action => 'task '||trans_rec.task_id|| ' fire time', + action_pretty => trans_rec.transition_name || ' automatically finished', + msg => 'Timed transition fired.' + ); + + /* Fire the transition */ + + fire_transition_internal( + task_id => trans_rec.task_id, + journal_id => v_journal_id + ); + + /* Update the workflow internal state */ + + sweep_automatic_transitions( + case_id => trans_rec.case_id, + journal_id => v_journal_id + ); + + end loop; + end sweep_timed_transitions; + + + /* This procedure should be scheduled to run as a dbms_job. */ + procedure sweep_hold_timeout + is + cursor tasks_to_cancel is + select t.task_id, t.case_id, tr.transition_name + from wf_tasks t, wf_transitions tr + where hold_timeout <= sysdate + and state = 'started' + and tr.workflow_key = t.workflow_key + and tr.transition_key = t.transition_key; + v_journal_id number; + begin + for task_rec in tasks_to_cancel loop + + /* Insert an entry to the journal so people will know it was canceled */ + + v_journal_id := journal_entry.new( + object_id => task_rec.case_id, + action => 'task '||task_rec.task_id||' cancel timeout', + action_pretty => task_rec.transition_name || ' timed out', + msg => 'The user''s hold on the task timed out and the task was automatically canceled' + ); + + + /* Cancel the task */ + + cancel_task( + task_id => task_rec.task_id, + journal_id => v_journal_id + ); + + end loop; + end sweep_hold_timeout; + + + + + procedure notify_assignee( + task_id in wf_tasks.task_id%TYPE, + user_id in users.user_id%TYPE, + callback in wf_context_transition_info.notification_callback%TYPE, + custom_arg in wf_context_transition_info.notification_custom_arg%TYPE + ) + is + v_deadline_pretty varchar2(400); + v_object_name varchar2(4000); + v_transition_key wf_transitions.transition_key%TYPE; + v_transition_name wf_transitions.transition_name%TYPE; + v_party_from parties.party_id%TYPE; + v_party_to parties.party_id%TYPE; + v_subject varchar2(4000); + v_body varchar2(4000); + v_request_id integer; + begin + select to_char(ta.deadline,'Mon fmDDfm, YYYY HH24:MI:SS'), + acs_object.name(c.object_id), + tr.transition_key, + tr.transition_name + into v_deadline_pretty, + v_object_name, + v_transition_key, + v_transition_name + from wf_tasks ta, wf_transitions tr, wf_cases c + where ta.task_id = notify_assignee.task_id + and c.case_id = ta.case_id + and tr.workflow_key = c.workflow_key + and tr.transition_key = ta.transition_key; + + /* Default values */ + v_party_from := -1; /* This default value should probably be pulled from somewhere */ + v_subject := 'Assignment: '||v_transition_name||' '||v_object_name; + v_body := 'You have been assigned to a task. +'||' +Task : '||v_transition_name||' +Object : '||v_object_name||' +'; + + if v_deadline_pretty != '' then + v_body := v_body ||'Deadline: '||v_deadline_pretty||' +'; + end if; + + /* We'd like to add a URL to go visit, but how do we get that URL? */ + /* The notifications should really be sent from the application server layer, not from the database */ + + if notify_assignee.callback is not null then + execute immediate 'begin '||notify_assignee.callback||'(:1, :2, :3, :4, :5, :6); end;' + using in notify_assignee.task_id, + in notify_assignee.custom_arg, + in notify_assignee.user_id, + in out v_party_from, + in out v_subject, + in out v_body; + end if; + + v_request_id := nt.post_request ( + party_from => v_party_from, + party_to => notify_assignee.user_id, + expand_group => 'f' , + subject => v_subject, + message => v_body, + max_retries => 3 + ); + + end notify_assignee; + + + + /* + * This procedure synchronizes the actually enabled transitions + * (i.e., the ones where tokens are currently present on the + * input places), with the cached version of that information, + * in wf_tasks. + * It is entirely idempotent and will at any given time sync up with + * the actual state of the workflow (as per the tokens), so we don't + * need to pass in a journal_id. + */ + procedure enable_transitions ( + case_id in number + ) is + cursor tasks_to_create is + select transition_key, + transition_name, + trigger_type, + enable_callback, + enable_custom_arg, + assignment_callback, + assignment_custom_arg, + time_callback, + time_custom_arg, + deadline_callback, + deadline_custom_arg, + deadline_attribute_name, + notification_callback, + notification_custom_arg, + unassigned_callback, + unassigned_custom_arg, + estimated_minutes + from wf_enabled_transitions et + where et.case_id = enable_transitions.case_id + and not exists (select 1 from wf_tasks + where case_id = enable_transitions.case_id + and transition_key = et.transition_key + and state in ('enabled', 'started')); + v_task_id number; + v_workflow_key varchar2(100); + v_trigger_time date; + v_deadline_date date; + v_party_from integer; + v_subject varchar2(500); + v_body varchar2(4000); + v_num_assigned number; + request_id nt_requests.request_id%TYPE; + cursor assignees_cur is + select distinct u.user_id + from wf_task_assignments tasgn, + party_approved_member_map m, + users u + where tasgn.task_id = v_task_id + and m.party_id = tasgn.party_id + and u.user_id = m.member_id; + begin + select workflow_key into v_workflow_key + from wf_cases + where case_id = enable_transitions.case_id; + + /* we mark tasks overridden if they were once enabled, but are no longer so */ + + update wf_tasks + set state = 'overridden', + overridden_date = sysdate + where case_id = enable_transitions.case_id + and state = 'enabled' + and transition_key not in + (select transition_key + from wf_enabled_transitions + where case_id = enable_transitions.case_id); + + + /* insert a task for the transitions that are enabled but have no task row */ + + for trans_rec in tasks_to_create loop + + v_trigger_time := null; + v_deadline_date := null; + + if trans_rec.trigger_type = 'user' then + v_deadline_date := get_task_deadline( + callback => trans_rec.deadline_callback, + custom_arg => trans_rec.deadline_custom_arg, + attribute_name => trans_rec.deadline_attribute_name, + case_id => enable_transitions.case_id, + transition_key => trans_rec.transition_key + ); + elsif trans_rec.trigger_type = 'time' then + v_trigger_time := execute_time_callback(trans_rec.time_callback, + trans_rec.time_custom_arg, + enable_transitions.case_id, trans_rec.transition_key); + end if; + + /* we're ready to insert the row */ + select wf_task_id_seq.nextval into v_task_id from dual; + + insert into wf_tasks ( + task_id, case_id, workflow_key, transition_key, + deadline, trigger_time, estimated_minutes + ) values ( + v_task_id, enable_transitions.case_id, v_workflow_key, + trans_rec.transition_key, + v_deadline_date, v_trigger_time, trans_rec.estimated_minutes + ); + + set_task_assignments( + task_id => v_task_id, + callback => trans_rec.assignment_callback, + custom_arg => trans_rec.assignment_custom_arg + ); + + /* Execute the transition enabled callback */ + execute_transition_callback( + callback => trans_rec.enable_callback, + custom_arg => trans_rec.enable_custom_arg, + case_id => enable_transitions.case_id, + transition_key => trans_rec.transition_key + ); + + select count(*) into v_num_assigned + from wf_task_assignments + where task_id = v_task_id; + + if v_num_assigned = 0 then + execute_unassigned_callback ( + callback => trans_rec.unassigned_callback, + task_id => v_task_id, + custom_arg => trans_rec.unassigned_custom_arg + ); + end if; + + end loop; + end enable_transitions; + + + procedure fire_transition_internal ( + task_id in number, + journal_id in number + ) is + v_case_id number; + v_state varchar2(40); + v_transition_key varchar2(100); + v_workflow_key varchar2(100); + v_place_key varchar2(100); + v_direction varchar2(3); + v_guard_happy_p char(1); + v_fire_callback varchar2(100); + v_fire_custom_arg varchar2(4000); + v_found_happy_guard char(1); + v_locked_task_id number; + begin + select t.case_id, t.state, t.workflow_key, t.transition_key, ti.fire_callback, ti.fire_custom_arg + into v_case_id, v_state, v_workflow_key, v_transition_key, v_fire_callback, v_fire_custom_arg + from wf_tasks t, wf_cases c, wf_transition_info ti + where t.task_id = fire_transition_internal.task_id + and c.case_id = t.case_id + and ti.context_key = c.context_key + and ti.workflow_key = c.workflow_key + and ti.transition_key = t.transition_key; + + /* Check that the state is either started or enabled */ + + if v_state = 'enabled' then + v_locked_task_id := null; + elsif v_state = 'started' then + v_locked_task_id := fire_transition_internal.task_id; + else + raise_application_error(-20000, 'Can''t fire the transition if it''s not in state enabled or started'); + end if; + + + /* Mark the task finished */ + + update wf_tasks + set state = 'finished', + finished_date = sysdate + where task_id = fire_transition_internal.task_id; + + + /* Consume the tokens */ + + for place_rec in input_places(v_workflow_key, v_transition_key) loop + consume_token( + case_id => v_case_id, + place_key => place_rec.place_key, + journal_id => fire_transition_internal.journal_id, + task_id => v_locked_task_id + ); + end loop; + + + /* Spit out new tokens in the output places */ + + v_found_happy_guard := 'f'; + for place_rec in output_places(v_workflow_key, v_transition_key) loop + v_place_key := place_rec.place_key; + v_direction := place_rec.direction; + v_guard_happy_p := evaluate_guard( + callback => place_rec.guard_callback, + custom_arg => place_rec.guard_custom_arg, + case_id => v_case_id, + workflow_key => v_workflow_key, + transition_key => v_transition_key, + place_key => v_place_key, + direction => v_direction + ); + + if v_guard_happy_p = 't' then + v_found_happy_guard := 't'; + add_token( + case_id => v_case_id, + place_key => place_rec.place_key, + journal_id => fire_transition_internal.journal_id + ); + end if; + end loop; + + + /* If we didn't find any happy guards, look for arcs with the special hash (#) guard */ + + if v_found_happy_guard = 'f' then + for place_rec in ( + select place_key + from wf_transition_places tp + where tp.workflow_key = v_workflow_key + and tp.transition_key = v_transition_key + and tp.direction = 'out' + and tp.guard_callback = '#') + loop + add_token( + case_id => v_case_id, + place_key => place_rec.place_key, + journal_id => fire_transition_internal.journal_id + ); + end loop; + end if; + + + /* Execute the transition fire callback */ + + execute_transition_callback( + callback => v_fire_callback, + custom_arg => v_fire_custom_arg, + case_id => v_case_id, + transition_key => v_transition_key + ); + end fire_transition_internal; + + + + /* A small helper to make sure we're in the state we expect to be */ + procedure ensure_task_in_state ( + task_id in number, + state in varchar2 + ) is + v_count number; + begin + select decode(count(*),0,0,1) into v_count + from wf_tasks + where task_id = ensure_task_in_state.task_id + and state = ensure_task_in_state.state; + + if v_count != 1 then + raise_application_error(-20000, 'The task '|| ensure_task_in_state.task_id || ' is not in state ''' || ensure_task_in_state.state || ''''); + end if; + end ensure_task_in_state; + + + + /* Marks a task started and reserves one token from each input place */ + procedure start_task( + task_id in number, + user_id in number, + journal_id in number + ) is + v_case_id number; + v_workflow_key wf_workflows.workflow_key%TYPE; + v_transition_key varchar2(100); + v_hold_timeout_callback varchar2(100); + v_hold_timeout_custom_arg varchar2(4000); + v_hold_timeout date; + begin + ensure_task_in_state(task_id => start_task.task_id, state => 'enabled'); + + select t.case_id, t.workflow_key, t.transition_key, ti.hold_timeout_callback, ti.hold_timeout_custom_arg + into v_case_id, v_workflow_key, v_transition_key, v_hold_timeout_callback, v_hold_timeout_custom_arg + from wf_tasks t, wf_cases c, wf_transition_info ti + where t.task_id = start_task.task_id + and c.case_id = t.case_id + and ti.context_key = c.context_key + and ti.workflow_key = t.workflow_key + and ti.transition_key = t.transition_key; + + v_hold_timeout := execute_hold_timeout_callback(v_hold_timeout_callback, + v_hold_timeout_custom_arg, v_case_id, v_transition_key); + + /* Mark it started */ + + update wf_tasks + set state = 'started', + started_date = sysdate, + holding_user = start_task.user_id, + hold_timeout = v_hold_timeout + where task_id = start_task.task_id; + + + /* Reserve one token from each input place */ + + for place_rec in input_places(v_workflow_key,v_transition_key) loop + lock_token( + case_id => v_case_id, + place_key => place_rec.place_key, + journal_id => start_task.journal_id, + task_id => start_task.task_id + ); + end loop; + end start_task; + + + /* Mark the task canceled and release the reserved tokens */ + procedure cancel_task( + task_id in number, + journal_id in number + ) + is + v_case_id number; + begin + ensure_task_in_state(task_id => cancel_task.task_id, state => 'started'); + select case_id into v_case_id + from wf_tasks + where task_id = cancel_task.task_id; + + /* Mark the task canceled */ + + update wf_tasks + set state = 'canceled', + canceled_date = sysdate + where task_id = cancel_task.task_id; + + + /* Release our reserved tokens */ + + release_token( + task_id => cancel_task.task_id, + journal_id => cancel_task.journal_id + ); + + /* The workflow state has now changed, so we must run this */ + + sweep_automatic_transitions( + case_id => v_case_id, + journal_id => cancel_task.journal_id + ); + end cancel_task; + + + /* Fire the transition */ + procedure finish_task ( + task_id in number, + journal_id in number + ) + is + v_case_id number; + begin + select case_id into v_case_id + from wf_tasks + where task_id = finish_task.task_id; + + fire_transition_internal( + task_id => finish_task.task_id, + journal_id => finish_task.journal_id + ); + + sweep_automatic_transitions( + case_id => v_case_id, + journal_id => finish_task.journal_id + ); + end finish_task; + + +end workflow_case; +/ +show errors; + + +-- +-- schedule processing of the notification queue +-- + +begin + nt.schedule_process(1,'localhost',25); +end; +/ +show errors; Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/workflow-package.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/workflow-package.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/workflow-package.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,532 @@ + -- +-- acs-workflow/sql/workflow-package.sql +-- +-- Creates the PL/SQL package that provides the API for defining and dropping +-- workflow cases. +-- +-- @author Lars Pind (lars@pinds.com) +-- +-- @creation-date 2000-05-18 +-- +-- @cvs-id $Id: workflow-package.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + + +create or replace package workflow +as + + /* Call this function after you have created a table */ + function create_workflow ( + workflow_key in varchar2, + pretty_name in varchar2, + pretty_plural in varchar2 default null, + description in varchar2 default null, + table_name in varchar2, + id_column in varchar2 default 'case_id' + ) return varchar2; + + procedure drop_workflow ( + workflow_key in varchar2 + ); + + procedure delete_cases ( + workflow_key in varchar2 + ); + + function create_attribute ( + workflow_key in varchar2, + attribute_name in varchar2, + datatype in varchar2, + pretty_name in varchar2, + pretty_plural in varchar2 default null, + table_name in varchar2 default null, + column_name in varchar2 default null, + default_value in varchar2 default null, + min_n_values in integer default 1, + max_n_values in integer default 1, + sort_order in integer default null, + storage in varchar2 default 'generic', + wf_datatype in varchar2 default 'none' + ) return acs_attributes.attribute_id%TYPE; + + procedure drop_attribute ( + workflow_key in varchar2, + attribute_name in varchar2 + ); + + procedure add_place ( + workflow_key in acs_object_types.object_type%TYPE, + place_key in wf_places.place_key%TYPE, + place_name in wf_places.place_name%TYPE, + sort_order in wf_places.sort_order%TYPE + ); + + procedure delete_place ( + workflow_key in acs_object_types.object_type%TYPE, + place_key in wf_places.place_key%TYPE + ); + + procedure add_transition ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_transitions.transition_key%TYPE, + transition_name in wf_transitions.transition_name%TYPE, + sort_order in wf_transitions.sort_order%TYPE, + trigger_type in wf_transitions.trigger_type%TYPE default 'user' + ); + + procedure delete_transition ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_transitions.transition_key%TYPE + ); + + procedure add_arc ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_arcs.transition_key%TYPE, + place_key in wf_arcs.place_key%TYPE, + direction in wf_arcs.direction%TYPE, + guard_callback in wf_arcs.guard_callback%TYPE default null, + guard_custom_arg in wf_arcs.guard_custom_arg%TYPE default null, + guard_description in wf_arcs.guard_description%TYPE default null + ); + + procedure delete_arc ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_arcs.transition_key%TYPE, + place_key in wf_arcs.place_key%TYPE, + direction in wf_arcs.direction%TYPE + ); + + /* + * A simple workflow is essentially one that we can display nicely using HTML tables. + * More specifically, it's a workflow containing only sequential routing and + * simple iteration, where the choice is always between moving to the next task + * in the sequence or looping back to some prior task. + */ + + function simple_p ( + workflow_key in varchar2 + ) return char; + +end workflow; +/ +show errors + +create or replace package body workflow +as + + function create_workflow ( + workflow_key in varchar2, + pretty_name in varchar2, + pretty_plural in varchar2 default null, + description in varchar2 default null, + table_name in varchar2, + id_column in varchar2 default 'case_id' + ) + return varchar2 + is + v_num_rows number; + v_workflow_key varchar2(100); + begin + select count(*) into v_num_rows from user_tables where table_name = upper(create_workflow.table_name); + if v_num_rows = 0 then + raise_application_error(-20000, 'The table '''||create_workflow.table_name||''' must be created before calling workflow.create_workflow.'); + end if; + + if substr(create_workflow.workflow_key, length(create_workflow.workflow_key) - 2, 3) != '_wf' then + v_workflow_key := create_workflow.workflow_key||'_wf'; + else + v_workflow_key := create_workflow.workflow_key; + end if; + + acs_object_type.create_type( + object_type => v_workflow_key, + pretty_name => create_workflow.pretty_name, + pretty_plural => create_workflow.pretty_plural, + supertype => 'workflow', + table_name => create_workflow.table_name, + id_column => create_workflow.id_column + ); + + insert into wf_workflows + (workflow_key, description) + values + (v_workflow_key, create_workflow.description); + + return v_workflow_key; + end create_workflow; + + + /* Note: The workflow-specific cases table must be dropped before calling this proc */ + procedure drop_workflow ( + workflow_key in varchar2 + ) + is + v_table_name varchar2(100); + v_num_rows number; + cursor attribute_cur is + select attribute_id, attribute_name from acs_attributes where object_type = drop_workflow.workflow_key; + begin + select table_name into v_table_name + from acs_object_types + where object_type = drop_workflow.workflow_key; + + select decode(count(*),0,0,1) into v_num_rows + from user_tables + where table_name = upper(v_table_name); + + if v_num_rows > 0 then + raise_application_error(-20000, 'The table '''||v_table_name||''' must be dropped before calling workflow.drop_workflow.'); + end if; + + select decode(count(*),0,0,1) into v_num_rows + from wf_cases + where workflow_key = drop_workflow.workflow_key; + + if v_num_rows > 0 then + raise_application_error(-20000, 'You must delete all cases of workflow '''||drop_workflow.workflow_key||''' before dropping the workflow definition.'); + end if; + + /* Delete all the auxillary stuff */ + delete from wf_context_task_panels where workflow_key = drop_workflow.workflow_key; + delete from wf_context_assignments where workflow_key = drop_workflow.workflow_key; + delete from wf_context_transition_info where workflow_key = drop_workflow.workflow_key; + delete from wf_arcs where workflow_key = drop_workflow.workflow_key; + delete from wf_places where workflow_key = drop_workflow.workflow_key; + delete from wf_transition_attribute_map where workflow_key = drop_workflow.workflow_key; + delete from wf_transition_assignment_map where workflow_key = drop_workflow.workflow_key; + delete from wf_transitions where workflow_key = drop_workflow.workflow_key; + + /* Drop all attributes */ + for attribute_rec in attribute_cur loop + /* there's no on delete cascade, so we have to manually delete all the values */ + delete from acs_attribute_values where attribute_id = attribute_rec.attribute_id; + + drop_attribute( + workflow_key => drop_workflow.workflow_key, + attribute_name => attribute_rec.attribute_name + ); + end loop; + + /* Delete the workflow */ + delete from wf_workflows where workflow_key = drop_workflow.workflow_key; + + acs_object_type.drop_type( + object_type => drop_workflow.workflow_key + ); + end drop_workflow; + + + procedure delete_cases ( + workflow_key in varchar2 + ) + is + cursor workflow_cases is select case_id from wf_cases where workflow_key = delete_cases.workflow_key; + begin + for case_rec in workflow_cases loop + workflow_case.delete(case_rec.case_id); + end loop; + end delete_cases; + + + function create_attribute ( + workflow_key in varchar2, + attribute_name in varchar2, + datatype in varchar2, + pretty_name in varchar2, + pretty_plural in varchar2 default null, + table_name in varchar2 default null, + column_name in varchar2 default null, + default_value in varchar2 default null, + min_n_values in integer default 1, + max_n_values in integer default 1, + sort_order in integer default null, + storage in varchar2 default 'generic', + wf_datatype in varchar2 default 'none' + ) return acs_attributes.attribute_id%TYPE + is + v_attribute_id number; + begin + v_attribute_id := acs_attribute.create_attribute( + object_type => create_attribute.workflow_key, + attribute_name => create_attribute.attribute_name, + datatype => create_attribute.datatype, + pretty_name => create_attribute.pretty_name, + pretty_plural => create_attribute.pretty_plural, + table_name => create_attribute.table_name, + column_name => create_attribute.column_name, + default_value => create_attribute.default_value, + min_n_values => create_attribute.min_n_values, + max_n_values => create_attribute.max_n_values, + sort_order => create_attribute.sort_order, + storage => create_attribute.storage + ); + + insert into wf_attribute_info + (attribute_id, wf_datatype) + values + (v_attribute_id, create_attribute.wf_datatype); + + return v_attribute_id; + end create_attribute; + + + procedure drop_attribute ( + workflow_key in varchar2, + attribute_name in varchar2 + ) + is + v_attribute_id number; + begin + select attribute_id into v_attribute_id + from acs_attributes + where object_type = drop_attribute.workflow_key + and attribute_name = drop_attribute.attribute_name; + + delete from wf_attribute_info where attribute_id = v_attribute_id; + + acs_attribute.drop_attribute( + object_type => drop_attribute.workflow_key, + attribute_name => drop_attribute.attribute_name + ); + end drop_attribute; + + + procedure add_place ( + workflow_key in acs_object_types.object_type%TYPE, + place_key in wf_places.place_key%TYPE, + place_name in wf_places.place_name%TYPE, + sort_order in wf_places.sort_order%TYPE + ) + is + begin + insert into wf_places (workflow_key, place_key, place_name, sort_order) + values (add_place.workflow_key, add_place.place_key, add_place.place_name, add_place.sort_order); + end add_place; + + procedure delete_place ( + workflow_key in acs_object_types.object_type%TYPE, + place_key in wf_places.place_key%TYPE + ) + is + begin + delete from wf_places + where workflow_key = delete_place.workflow_key + and place_key = delete_place.place_key; + end delete_place; + + procedure add_transition ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_transitions.transition_key%TYPE, + transition_name in wf_transitions.transition_name%TYPE, + sort_order in wf_transitions.sort_order%TYPE, + trigger_type in wf_transitions.trigger_type%TYPE default 'user' + ) + is + begin + insert into wf_transitions (workflow_key, transition_key, transition_name, sort_order, trigger_type) + values (add_transition.workflow_key, add_transition.transition_key, add_transition.transition_name, + add_transition.sort_order, add_transition.trigger_type); + end add_transition; + + procedure delete_transition ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_transitions.transition_key%TYPE + ) + is + begin + delete from wf_transitions + where workflow_key = delete_transition.workflow_key + and transition_key = delete_transition.transition_key; + end delete_transition; + + procedure add_arc ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_arcs.transition_key%TYPE, + place_key in wf_arcs.place_key%TYPE, + direction in wf_arcs.direction%TYPE, + guard_callback in wf_arcs.guard_callback%TYPE default null, + guard_custom_arg in wf_arcs.guard_custom_arg%TYPE default null, + guard_description in wf_arcs.guard_description%TYPE default null + ) + is + begin + insert into wf_arcs (workflow_key, transition_key, place_key, direction, + guard_callback, guard_custom_arg, guard_description) + values (add_arc.workflow_key, add_arc.transition_key, add_arc.place_key, add_arc.direction, + add_arc.guard_callback, add_arc.guard_custom_arg, add_arc.guard_description); + end add_arc; + + procedure delete_arc ( + workflow_key in acs_object_types.object_type%TYPE, + transition_key in wf_arcs.transition_key%TYPE, + place_key in wf_arcs.place_key%TYPE, + direction in wf_arcs.direction%TYPE + ) + is + begin + delete from wf_arcs + where workflow_key = delete_arc.workflow_key + and transition_key = delete_arc.transition_key + and place_key = delete_arc.place_key + and direction = delete_arc.direction; + end delete_arc; + + + + function simple_p ( + workflow_key in varchar2 + ) + return char + is + type t_place_table is table of wf_places.place_key%TYPE + index by binary_integer; + type t_guard_table is table of wf_arcs.guard_callback%TYPE + index by binary_integer; + + previous_place_list t_place_table; + target_place_list t_place_table; + guard_list t_guard_table; + row_count integer := 0; + v_count integer; + v_count2 integer; + v_place_key wf_places.place_key%TYPE; + v_end_place wf_places.place_key%TYPE; + v_transition_key wf_transitions.transition_key%TYPE; + + cursor transition_targets is + select place_key,guard_callback,rownum + from wf_arcs + where workflow_key = simple_p.workflow_key + and transition_key = v_transition_key + and direction = 'out'; + + begin + /* Let's do some simple checks first */ + + /* Places with more than one arc out */ + select count(*) into v_count + from wf_places p + where p.workflow_key = simple_p.workflow_key + and 1 < (select count(*) + from wf_arcs a + where a.workflow_key = p.workflow_key + and a.place_key = p.place_key + and direction = 'in'); + + if v_count > 0 then + return 'f'; + end if; + + /* Transitions with more than one arc in */ + select count(*) into v_count + from wf_transitions t + where t.workflow_key = simple_p.workflow_key + and 1 < (select count(*) + from wf_arcs a + where a.workflow_key = t.workflow_key + and a.transition_key = t.transition_key + and direction = 'in'); + + if v_count > 0 then + return 'f'; + end if; + + /* Transitions with more than two arcs out */ + select count(*) into v_count + from wf_transitions t + where t.workflow_key = simple_p.workflow_key + and 2 < (select count(*) + from wf_arcs a + where a.workflow_key = t.workflow_key + and a.transition_key = t.transition_key + and direction = 'out'); + + if v_count > 0 then + return 'f'; + end if; + + /* Now we do the more complicated checks. + * We keep a list of visited places because I couldn't think + * of a nicer way that wasn't susceptable to infinite loops. + */ + + + v_place_key := 'start'; + v_end_place := 'end'; + + loop + exit when v_place_key = v_end_place; + + previous_place_list(row_count) := v_place_key; + + select unique transition_key into v_transition_key + from wf_arcs + where workflow_key = simple_p.workflow_key + and place_key = v_place_key + and direction = 'in'; + + select count(*) into v_count + from wf_arcs + where workflow_key = simple_p.workflow_key + and transition_key = v_transition_key + and direction = 'out'; + + if v_count = 1 then + select unique place_key into v_place_key + from wf_arcs + where workflow_key = simple_p.workflow_key + and transition_key = v_transition_key + and direction = 'out'; + + elsif v_count = 0 then + /* deadend! */ + return 'f'; + + else + /* better be two based on our earlier test */ + + for v_target in transition_targets loop + target_place_list(v_target.rownum) := v_target.place_key; + guard_list(v_target.rownum) := v_target.guard_callback; + end loop; + + /* Check that the guard functions are the negation of each other + * by looking for the magic entry "#" (exactly once) + */ + if ((guard_list(1) != '#' and guard_list(2) != '#') or + (guard_list(1) = '#' and guard_list(2) = '#')) then + return 'f'; + end if; + + /* Check that exactly one of the targets is in the previous list */ + + v_count2 := 0; + for i in 0..row_count loop + if target_place_list(1) = previous_place_list(i) then + v_count2 := v_count2 + 1; + v_place_key := target_place_list(2); + end if; + if target_place_list(2) = previous_place_list(i) then + v_count2 := v_count2 + 1; + v_place_key := target_place_list(1); + end if; + end loop; + + if v_count2 != 1 then + return 'f'; + end if; + + end if; + + row_count := row_count + 1; + + end loop; + + /* if we got here, it must be okay */ + return 't'; + + end simple_p; + +end workflow; +/ +show errors Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/test/workflow-case-package-test.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/test/workflow-case-package-test.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/test/workflow-case-package-test.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,516 @@ +set serveroutput on size 1000000 format wrapped + +-- requires the utPLSQL system +-- +-- modify this line to suit your needs +-- +-- exec utplsql.setdir('/web/lars-dev2/packages/acs-kernel/sql'); + +exec utplsql.autocompile (false); + +create or replace package ut#workflow_case +as + + procedure setup; + + procedure teardown; + + procedure run; + +end ut#workflow_case; +/ +show errors + +create or replace package body ut#workflow_case +as + + procedure setup + is + wf_count number; + begin + teardown; + dbms_output.put_line('Setting up...'); + + /* We assume that the sample-expenses workflow is loaded and unchanged from the original */ + select decode(count(*),0,0,1) into wf_count from wf_workflows where workflow_key = 'expenses_wf'; + if wf_count = 0 then + raise_application_error(-20000, 'The sample-expenses workflow must be loaded (and unchanged from the original)'); + end if; + + + + utplsql.setpkg('workflow_case'); + utplsql.addtest('run'); + end; + + procedure teardown + is + begin + dbms_output.put_line('Tearing down...'); + + end; + + procedure run + is + v_workflow_key wf_workflows.workflow_key%TYPE; + v_object_id acs_objects.object_id%TYPE; + v_case_id wf_cases.case_id%TYPE; + v_count number; + v_task_id number; + v_journal_id number; + v_user_id number; + v_state varchar2(100); + begin + v_workflow_key := 'expenses_wf'; + + dbms_output.put_line('Running test...'); + + /* Pick out a random object ... we just hope there is one somewhere */ + select object_id into v_object_id from acs_objects where rownum = 1; + + dbms_output.put_line('. new case'); + + v_case_id := workflow_case.new( + workflow_key => v_workflow_key, + context_key => 'default', + object_id => v_object_id + ); + + dbms_output.put_line('. manual assignments'); + + /* we need a random user_id */ + select user_id into v_user_id + from users + where rownum = 1; + + workflow_case.add_manual_assignment ( + case_id => v_case_id, + transition_key => 'assign', + party_id => v_user_id + ); + + workflow_case.add_manual_assignment ( + case_id => v_case_id, + transition_key => 'supervisor_approval', + party_id => v_user_id + ); + + workflow_case.add_manual_assignment ( + case_id => v_case_id, + transition_key => 'other_approval', + party_id => v_user_id + ); + + workflow_case.add_manual_assignment ( + case_id => v_case_id, + transition_key => 'buy', + party_id => v_user_id + ); + + + + dbms_output.put_line('. start case'); + + workflow_case.start_case( + case_id => v_case_id + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and transition_key = 'assign' + and state = 'enabled'; + + utassert.eq( + msg_in => 'We should have exactly one ''assign'' task enabled', + check_this_in => 1, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id; + + utassert.eq( + msg_in => 'The ''assign'' task should be the only task there is for this case yet.', + check_this_in => 1, + against_this_in => v_count + ); + + /* Get that task_id */ + select task_id into v_task_id + from wf_tasks + where case_id = v_case_id + and transition_key = 'assign' + and state = 'enabled'; + + + + + dbms_output.put_line('. start task ''assign'''); + + v_journal_id := workflow_case.begin_task_action( + task_id => v_task_id, + action => 'start', + action_ip => '0.0.0.0', + user_id => v_user_id, + msg => 'regression-test: started task ''assign''' + ); + + workflow_case.end_task_action( + journal_id => v_journal_id, + action => 'start', + task_id => v_task_id + ); + + select state into v_state + from wf_tasks + where task_id = v_task_id; + + utassert.eq( + msg_in => 'The ''assign'' task should be in state ''started''.', + check_this_in => 'started', + against_this_in => v_state + ); + + + + + dbms_output.put_line('. cancel task ''assign'''); + + v_journal_id := workflow_case.begin_task_action( + task_id => v_task_id, + action => 'cancel', + action_ip => '0.0.0.0', + user_id => v_user_id, + msg => 'regression-test: canceled task ''assign''' + ); + + workflow_case.end_task_action( + journal_id => v_journal_id, + action => 'cancel', + task_id => v_task_id + ); + + select state into v_state + from wf_tasks + where task_id = v_task_id; + + utassert.eq( + msg_in => 'The ''assign'' task should be in state ''canceled''.', + check_this_in => 'canceled', + against_this_in => v_state + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and transition_key = 'assign' + and state = 'enabled'; + + utassert.eq( + msg_in => 'We should have exactly one ''assign'' task enabled', + check_this_in => 1, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id; + + utassert.eq( + msg_in => 'There should be exactly two tasks for this case, one enabled and one canceled.', + check_this_in => 2, + against_this_in => v_count + ); + + + + + dbms_output.put_line('. finish task ''assign'''); + + /* Get that task_id for the 'assign' task */ + select task_id into v_task_id + from wf_tasks + where case_id = v_case_id + and transition_key = 'assign' + and state = 'enabled'; + + v_journal_id := workflow_case.begin_task_action( + task_id => v_task_id, + action => 'finish', + action_ip => '0.0.0.0', + user_id => v_user_id, + msg => 'regression-test: finished task ''assign''' + ); + + workflow_case.end_task_action( + journal_id => v_journal_id, + action => 'finish', + task_id => v_task_id + ); + + select state into v_state + from wf_tasks + where task_id = v_task_id; + + utassert.eq( + msg_in => 'The ''assign'' task should be in state ''finished''.', + check_this_in => 'finished', + against_this_in => v_state + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and transition_key = 'supervisor_approval' + and state = 'enabled'; + + utassert.eq( + msg_in => 'We should have exactly one ''supervisor_approval'' task enabled', + check_this_in => 1, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and transition_key = 'other_approval' + and state = 'enabled'; + + utassert.eq( + msg_in => 'We should have exactly one ''other_approval'' task enabled', + check_this_in => 1, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id; + + utassert.eq( + msg_in => 'There should be exactly five tasks for this case, one canceled, two finished, and two enabled.', + check_this_in => 5, + against_this_in => v_count + ); + + + dbms_output.put_line('. finish task ''supervisor_approval'' without starting it first (saying okay)'); + + /* Get the task_id for the supervisor_approval task */ + select task_id into v_task_id + from wf_tasks + where case_id = v_case_id + and transition_key = 'supervisor_approval' + and state = 'enabled'; + + v_journal_id := workflow_case.begin_task_action( + task_id => v_task_id, + action => 'finish', + action_ip => '0.0.0.0', + user_id => v_user_id, + msg => 'regression-test: finished task ''supervisor_approval''' + ); + + workflow_case.set_attribute_value( + journal_id => v_journal_id, + attribute_name => 'supervisor_ok', + value => 't' + ); + + workflow_case.end_task_action( + journal_id => v_journal_id, + action => 'finish', + task_id => v_task_id + ); + + select state into v_state + from wf_tasks + where task_id = v_task_id; + + utassert.eq( + msg_in => 'The ''supervisor_approval'' task should be in state ''finished''.', + check_this_in => 'finished', + against_this_in => v_state + ); + + + + dbms_output.put_line('. finish task ''other_approval'' without starting it first (saying okay)'); + + /* Get the task_id for the other_approval task */ + select task_id into v_task_id + from wf_tasks + where case_id = v_case_id + and transition_key = 'other_approval' + and state = 'enabled'; + + v_journal_id := workflow_case.begin_task_action( + task_id => v_task_id, + action => 'finish', + action_ip => '0.0.0.0', + user_id => v_user_id, + msg => 'regression-test: finished task ''other_approval''' + ); + + workflow_case.set_attribute_value( + journal_id => v_journal_id, + attribute_name => 'other_ok', + value => 't' + ); + + workflow_case.end_task_action( + journal_id => v_journal_id, + action => 'finish', + task_id => v_task_id + ); + + select state into v_state + from wf_tasks + where task_id = v_task_id; + + utassert.eq( + msg_in => 'The ''other_approval'' task should be in state ''finished''.', + check_this_in => 'finished', + against_this_in => v_state + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and state = 'enabled'; + + utassert.eq( + msg_in => 'We should have exactly one task enabled', + check_this_in => 1, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and transition_key = 'buy' + and state = 'enabled'; + + utassert.eq( + msg_in => 'We should have the ''buy'' task enabled', + check_this_in => 1, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id; + + utassert.eq( + msg_in => 'There should be exactly seven tasks for this case, one canceled, six finished, and one enabled.', + check_this_in => 7, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and state = 'finished'; + + utassert.eq( + msg_in => 'There should be exactly five finished tasks', + check_this_in => 5, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and state = 'canceled'; + + utassert.eq( + msg_in => 'There should be exactly one canceled task', + check_this_in => 1, + against_this_in => v_count + ); + + + dbms_output.put_line('. finish task ''buy'''); + + /* Get that task_id for the 'buy' task */ + select task_id into v_task_id + from wf_tasks + where case_id = v_case_id + and transition_key = 'buy' + and state = 'enabled'; + + v_journal_id := workflow_case.begin_task_action( + task_id => v_task_id, + action => 'finish', + action_ip => '0.0.0.0', + user_id => v_user_id, + msg => 'regression-test: finished task ''buy''' + ); + + workflow_case.end_task_action( + journal_id => v_journal_id, + action => 'finish', + task_id => v_task_id + ); + + select state into v_state + from wf_tasks + where task_id = v_task_id; + + utassert.eq( + msg_in => 'The ''buy'' task should be in state ''finished''.', + check_this_in => 'finished', + against_this_in => v_state + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id; + + utassert.eq( + msg_in => 'There should be exactly seven tasks for this case, one canceled, six finished, and one enabled.', + check_this_in => 7, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and state = 'finished'; + + utassert.eq( + msg_in => 'There should be exactly six finished tasks', + check_this_in => 6, + against_this_in => v_count + ); + + select count(*) into v_count + from wf_tasks + where case_id = v_case_id + and state = 'canceled'; + + utassert.eq( + msg_in => 'There should be exactly one canceled task', + check_this_in => 1, + against_this_in => v_count + ); + + select state into v_state + from wf_cases + where case_id = v_case_id; + + utassert.eq( + msg_in => 'The case should be finished', + check_this_in => 'finished', + against_this_in => v_state + ); + + utresult.show; + end; + +end ut#workflow_case; +/ +show errors + +exec utplsql.test('workflow_case'); \ No newline at end of file Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade/upgrade-4.0-4.0.1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade/upgrade-4.0-4.0.1.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade/upgrade-4.0-4.0.1.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,346 @@ +-- Recreating almost all foreign key constraints in order to add "on delete cascade" + +-- wf_workflows + +alter table wf_workflows drop constraint wf_workflows_workflow_key_fk; +alter table wf_workflows add ( + constraint wf_workflows_workflow_key_fk + foreign key(workflow_key) references acs_object_types(object_type) on delete cascade +); + +-- wf_places + +alter table wf_places drop constraint wf_place_workflow_fk; +alter table wf_places add ( + constraint wf_place_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +-- wf_transitions + +alter table wf_transitions drop constraint wf_transition_workflow_fk; +alter table wf_transitions add ( + constraint wf_transition_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +-- wf_arcs + +alter table wf_arcs drop constraint wf_ts_arc_workflow_fk; +alter table wf_arcs add ( + constraint wf_ts_arc_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_arcs drop constraint wf_arc_transition_fk; +alter table wf_arcs add ( + constraint wf_arc_transition_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +alter table wf_arcs drop constraint wf_arc_place_fk; +alter table wf_arcs add ( + constraint wf_arc_place_fk + foreign key(workflow_key, place_key) references wf_places(workflow_key, place_key) on delete cascade +); + +create index wf_arcs_wf_key_trans_key_idx on wf_arcs(workflow_key, transition_key); +create index wf_arcs_wf_key_place_key_idx on wf_arcs(workflow_key, place_key); + +-- wf_attribute_info + +alter table wf_attribute_info add ( + constraint wf_attribute_info_attribute_pk primary key(attribute_id) +); + +alter table wf_attribute_info drop constraint wf_attribute_info_attribute_fk; +alter table wf_attribute_info add ( + constraint wf_attribute_info_attribute_fk + foreign key(attribute_id) references acs_attributes(attribute_id) on delete cascade +); + +-- wf_transition_attribute_map + +alter table wf_transition_attribute_map drop constraint wf_trans_attr_map_workflow_fk; +alter table wf_transition_attribute_map add ( + constraint wf_trans_attr_map_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_transition_attribute_map drop constraint wf_trans_attr_map_trans_fk; +alter table wf_transition_attribute_map add ( + constraint wf_trans_attr_map_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +-- wf_transition_assignment_map + +alter table wf_transition_assignment_map drop constraint wf_trans_asgn_map_workflow_fk; +alter table wf_transition_assignment_map add ( + constraint wf_trans_asgn_map_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_transition_assignment_map drop constraint wf_trans_asgn_map_trans_fk; +alter table wf_transition_assignment_map add ( + constraint wf_trans_asgn_map_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +alter table wf_transition_assignment_map drop constraint wf_tr_asgn_map_asgn_trans_fk; +alter table wf_transition_assignment_map add ( + constraint wf_tr_asgn_map_asgn_trans_fk + foreign key(workflow_key, assign_transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_trans_asgn_map_wf_trans_idx on wf_transition_assignment_map(workflow_key, transition_key); +create index wf_trans_asgn_map_wf_as_tr_idx on wf_transition_assignment_map(workflow_key, assign_transition_key); + +-- wf_context_transition_info + +alter table wf_context_transition_info drop constraint wf_context_trans_trans_fk; +alter table wf_context_transition_info add ( + constraint wf_context_trans_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_ctx_trans_wf_trans_idx on wf_context_transition_info(workflow_key, transition_key); + +-- wf_context_assignments + +alter table wf_context_assignments drop constraint wf_context_assign_context_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_context_fk + foreign key(context_key) references wf_contexts(context_key) on delete cascade +); + +alter table wf_context_assignments drop constraint wf_context_assign_workflow_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_context_assignments drop constraint wf_context_assign_party_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_party_fk + foreign key(party_id) references parties(party_id) on delete cascade +); + +alter table wf_context_assignments drop constraint wf_context_assign_trans_fk; +alter table wf_context_assignments add ( + constraint wf_context_assign_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_ctx_assg_workflow_trans_idx on wf_context_assignments(workflow_key, transition_key); + +-- wf_cases + +alter table wf_cases drop constraint wf_cases_acs_object_fk; +alter table wf_cases add ( + constraint wf_cases_acs_object_fk + foreign key(case_id) references acs_objects(object_id) on delete cascade +); + +alter table wf_cases drop constraint wf_cases_workflow_fk; +alter table wf_cases add ( + constraint wf_cases_workflow_fk + foreign key(workflow_key) references wf_workflows(workflow_key) on delete cascade +); + +alter table wf_cases drop constraint wf_cases_context_fk; +alter table wf_cases add ( + constraint wf_cases_context_fk + foreign key(context_key) references wf_contexts(context_key) on delete cascade +); + +alter table wf_cases drop constraint wf_cases_object_fk; +alter table wf_cases add ( + constraint wf_cases_object_fk + foreign key(object_id) references acs_objects(object_id) on delete cascade +); + +create index wf_cases_workflow_key_idx on wf_cases(workflow_key); +create index wf_cases_context_key_idx on wf_cases(context_key); +create index wf_cases_object_id_idx on wf_cases(object_id); + +-- wf_case_assignments + +alter table wf_case_assignments drop constraint wf_case_assign_fk; +alter table wf_case_assignments add ( + constraint wf_case_assign_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_case_assignments drop constraint wf_case_assign_party_fk; +alter table wf_case_assignments add ( + constraint wf_case_assign_party_fk + foreign key(party_id) references parties(party_id) on delete cascade +); + +alter table wf_case_assignments drop constraint wf_case_assign_trans_fk; +alter table wf_case_assignments add ( + constraint wf_case_assign_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +create index wf_case_assgn_party_idx on wf_case_assignments(party_id); + +-- wf_case_deadlines + +alter table wf_case_deadlines drop constraint wf_case_deadline_fk; +alter table wf_case_deadlines add ( + constraint wf_case_deadline_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_case_deadlines drop constraint wf_case_deadline_trans_fk; +alter table wf_case_deadlines add ( + constraint wf_case_deadline_trans_fk + foreign key(workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) on delete cascade +); + +-- wf_tasks + +alter table wf_tasks drop constraint wf_task_case_fk; +alter table wf_tasks add ( + constraint wf_task_case_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_tasks drop constraint wf_task_holding_user_fk; +alter table wf_tasks add ( + constraint wf_task_holding_user_fk + foreign key(holding_user) references users(user_id) on delete cascade +); + +create index wf_tasks_case_id_idx on wf_tasks(case_id); +create index wf_tasks_holding_user_idx on wf_tasks(holding_user); + +-- wf_task_assignments + +alter table wf_task_assignments drop constraint wf_task_assign_task_fk; +alter table wf_task_assignments add ( + constraint wf_task_assign_task_fk + foreign key(task_id) references wf_tasks(task_id) on delete cascade +); + +alter table wf_task_assignments drop constraint wf_task_party_fk; +alter table wf_task_assignments add ( + constraint wf_task_party_fk + foreign key(party_id) references parties(party_id) on delete cascade +); + +create index wf_task_asgn_party_id_idx on wf_task_assignments(party_id); + +-- wf_tokens + +alter table wf_tokens drop constraint wf_token_workflow_instance_fk; +alter table wf_tokens add ( + constraint wf_token_workflow_instance_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +create index wf_tokens_case_id_idx on wf_tokens(case_id); + +-- wf_attribute_value_audit + +alter table wf_attribute_value_audit drop constraint wf_attr_val_audit_case_fk; +alter table wf_attribute_value_audit add ( + constraint wf_attr_val_audit_case_fk + foreign key(case_id) references wf_cases(case_id) on delete cascade +); + +alter table wf_attribute_value_audit drop constraint wf_attr_val_audit_attr_fk; +alter table wf_attribute_value_audit add ( + constraint wf_attr_val_audit_attr_fk + foreign key(attribute_id) references acs_attributes(attribute_id) on delete cascade +); + +create index wf_attr_val_aud_attr_id_idx on wf_attribute_value_audit(attribute_id); + + +-- Added a column to wf_tasks + +alter table wf_tasks add (estimated_minutes integer); + + +-- We've added the column estimated_minutes to this view. + +create or replace view wf_enabled_transitions as +select c.case_id, + t.transition_key, + t.transition_name, + t.workflow_key, + t.sort_order, + t.trigger_type, + t.context_key, + t.enable_callback, + t.enable_custom_arg, + t.fire_callback, + t.fire_custom_arg, + t.assignment_callback, + t.assignment_custom_arg, + t.time_callback, + t.time_custom_arg, + t.deadline_callback, + t.deadline_custom_arg, + t.deadline_attribute_name, + t.hold_timeout_callback, + t.hold_timeout_custom_arg, + t.estimated_minutes + from wf_transition_info t, + wf_cases c + where t.workflow_key = c.workflow_key + and t.context_key = c.context_key + and c.state = 'active' + and not exists + (select tp.place_key + from wf_transition_places tp + where tp.transition_key = t.transition_key + and tp.workflow_key = t.workflow_key + and tp.direction = 'in' + and not exists + (select tk.token_id + from wf_tokens tk + where tk.place_key = tp.place_key + and tk.case_id = c.case_id + and tk.state = 'free' + ) + ); + + +-- We've added the column workflow_key to this view + +create or replace view wf_user_tasks as +select distinct ta.task_id, + ta.case_id, + ta.workflow_key, + ta.transition_key, + tr.transition_name, + ta.enabled_date, + ta.started_date, + u.user_id, + ta.state, + ta.holding_user, + ta.hold_timeout, + ta.deadline, + ta.estimated_minutes +from wf_tasks ta, + wf_task_assignments tasgn, + wf_cases c, + wf_transition_info tr, + party_approved_member_map m, + users u +where ta.state in ( 'enabled','started') +and c.case_id = ta.case_id +and c.state = 'active' +and tr.transition_key = ta.transition_key +and tr.trigger_type = 'user' +and tr.context_key = c.context_key +and tasgn.task_id = ta.task_id +and m.party_id = tasgn.party_id +and u.user_id = m.member_id; + + + Index: openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade/upgrade-4.0.1-4.1b.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade/upgrade-4.0.1-4.1b.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/obsolete-packages/acs-workflow/sql/oracle/upgrade/upgrade-4.0.1-4.1b.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,190 @@ +/* + * Panels callbacks have changed. + * + * Instead of a Tcl callback (sick idea, we knew that all along), + * we now store a list of URLs of templates to include. + */ + +/* Drop the old column */ +alter table wf_context_transition_info drop column panels_callback_tcl; + +/* Add the new table instead */ +create table wf_context_task_panels ( + context_key varchar2(100) not null + constraint wf_context_panels_context_fk + references wf_contexts(context_key) + on delete cascade, + workflow_key varchar2(100) not null + constraint wf_context_panels_workflow_fk + references wf_workflows(workflow_key) + on delete cascade, + transition_key varchar2(100) not null, + sort_key integer not null, + header varchar2(200) not null, + template_url varchar2(500) not null, + /* table constraints */ + constraint wf_context_panels_trans_fk + foreign key (workflow_key, transition_key) references wf_transitions(workflow_key, transition_key) + on delete cascade, + constraint wf_context_panels_pk + primary key (context_key, workflow_key, transition_key, sort_key) +); + +create index wf_ctx_panl_workflow_trans_idx on wf_context_task_panels(workflow_key, transition_key); + +comment on table wf_context_task_panels is ' + Holds information about the panels to be displayed on the task page. +'; + + + + +/* + * Added a Notification callback. + * + */ + +alter table wf_context_transition_info add ( + /* + * Notification callback + * Will be called when a notification is sent i.e., when a transition is enabled, + * or assignment changes. + * signature: (task_id in number, + * custom_arg in varchar2, + * party_to in integer, + * party_from in out integer, + * subject in out varchar2, + * body in out varchar2) + */ + notification_callback varchar2(100), + notification_custom_arg varchar2(4000) +); + + + + + +/* + * Enhanced comment on wf_cases.object_id + * + */ + +comment on column wf_cases.object_id is ' + A case is itself an acs_object, but moreover, a case will always be about some + other acs_object. E.g. for ticket-tracker, the case_id will refer to an instance of + the ticket-tracker-workflow, while the object_id will refer to the ticket itself. + It is possible to have multiple cases around the same object. +'; + + +/* + * Added unassigned callback + */ + +alter table wf_context_transition_info add ( + /* + * Unassigned callback + * Will be called whenever a task becomes unassigned + * Signature: (case_id in number, transition_key in varchar2, custom_arg in varchar2) + */ + unassigned_callback varchar2(100), + unassigned_custom_arg varchar2(4000) +); + + +/* + * Added access privilege + */ + +alter table wf_context_transition_info add ( + /* name of the privilege we should check before allowing access + * to task information. + */ + access_privilege varchar2(100) +); + + +/* panels_callback_tcl, notification_callback and unassigned_callback + * affect this view. + */ + +create or replace view wf_transition_info as +select t.transition_key, + t.transition_name, + t.workflow_key, + t.sort_order, + t.trigger_type, + t.context_key, + ct.estimated_minutes, + ct.enable_callback, + ct.enable_custom_arg, + ct.fire_callback, + ct.fire_custom_arg, + ct.assignment_callback, + ct.assignment_custom_arg, + ct.time_callback, + ct.time_custom_arg, + ct.deadline_callback, + ct.deadline_custom_arg, + ct.deadline_attribute_name, + ct.hold_timeout_callback, + ct.hold_timeout_custom_arg, + ct.notification_callback, + ct.notification_custom_arg, + ct.unassigned_callback, + ct.unassigned_custom_arg, + ct.access_privilege +from wf_transition_contexts t, wf_context_transition_info ct +where ct.workflow_key (+) = t.workflow_key + and ct.transition_key (+) = t.transition_key + and ct.context_key (+) = t.context_key; + + +/* Notifications and unassigned affect this */ + +create or replace view wf_enabled_transitions as +select c.case_id, + t.transition_key, + t.transition_name, + t.workflow_key, + t.sort_order, + t.trigger_type, + t.context_key, + t.enable_callback, + t.enable_custom_arg, + t.fire_callback, + t.fire_custom_arg, + t.assignment_callback, + t.assignment_custom_arg, + t.time_callback, + t.time_custom_arg, + t.deadline_callback, + t.deadline_custom_arg, + t.deadline_attribute_name, + t.hold_timeout_callback, + t.hold_timeout_custom_arg, + t.notification_callback, + t.notification_custom_arg, + t.unassigned_callback, + t.unassigned_custom_arg, + t.estimated_minutes, + t.access_privilege + from wf_transition_info t, + wf_cases c + where t.workflow_key = c.workflow_key + and t.context_key = c.context_key + and c.state = 'active' + and not exists + (select tp.place_key + from wf_transition_places tp + where tp.transition_key = t.transition_key + and tp.workflow_key = t.workflow_key + and tp.direction = 'in' + and not exists + (select tk.token_id + from wf_tokens tk + where tk.place_key = tp.place_key + and tk.case_id = c.case_id + and tk.state = 'free' + ) + ); Index: openacs-4/packages/acs-admin/acs-admin.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-admin/acs-admin.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-admin/acs-admin.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-admin/acs-admin.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Bryan Quinn Provides an interface for administering the Arsdigita Community System Installation. 2001-03-06 Index: openacs-4/packages/acs-admin/www/apm/file-add-2.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-admin/www/apm/Attic/file-add-2.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-admin/www/apm/file-add-2.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-admin/www/apm/file-add-2.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -18,13 +18,15 @@ set info [lindex $processed_files $index] set index_path [lindex $info 0] set file_type [lindex $info 1] + set db_type [lindex $info 2] +ns_log Notice "Huh? db_type: $db_type" # Do a doubleclick protection check. if { ![db_string apm_file_add_doubleclick_ck { select count(*) from apm_package_files where version_id = :version_id and path = :index_path } -default 0] } { - apm_file_add $version_id $index_path $file_type + apm_file_add $version_id $index_path $file_type $db_type } } apm_package_install_spec $version_id Index: openacs-4/packages/acs-admin/www/apm/file-add.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-admin/www/apm/Attic/file-add.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-admin/www/apm/file-add.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-admin/www/apm/file-add.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -46,8 +46,13 @@ doc_body_append "$relative_path (already registered to this package)\n" } else { set type [apm_guess_file_type $package_key $relative_path] - doc_body_append " $relative_path: [apm_pretty_name_for_file_type $type]\n" - lappend processed_files [list $relative_path $type] + set db_type [apm_guess_db_type $package_key $relative_path] + doc_body_append " $relative_path: [apm_pretty_name_for_file_type $type]" + if { ![empty_string_p $db_type] } { + doc_body_append " ([apm_pretty_name_for_db_type $db_type])" + } + doc_body_append "\n" + lappend processed_files [list $relative_path $type $db_type] } incr counter } Index: openacs-4/packages/acs-admin/www/apm/package-delete.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-admin/www/apm/package-delete.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-admin/www/apm/package-delete.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-admin/www/apm/package-delete.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -12,11 +12,14 @@ apm_version_info $version_id # Find the drop scripts. +set db_type [db_type] + db_foreach apm_package_drop_scripts { select file_id, path from apm_package_files where version_id = :version_id and file_type = 'data_model_drop' + and (db_type is null or db_type = :db_type) } { append file_list " Index: openacs-4/packages/acs-admin/www/apm/packages-install-3.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-admin/www/apm/packages-install-3.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-admin/www/apm/packages-install-3.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-admin/www/apm/packages-install-3.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -53,6 +53,7 @@ foreach file $data_model_files { set path [lindex $file 0] set file_type [lindex $file 1] + set db_type [lindex $file 2] append table_rows " $path Index: openacs-4/packages/acs-admin/www/apm/version-files.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-admin/www/apm/version-files.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-admin/www/apm/version-files.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-admin/www/apm/version-files.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -38,11 +38,17 @@ set last_components [list] set counter 0 + +doc_body_append "PathFile type + Database supportActions\n" + db_foreach apm_all_files { - select f.file_id, f.path, f.file_type, nvl(t.pretty_name, 'Unknown type') pretty_name - from apm_package_files f, apm_package_file_types t + select f.file_id, f.path, f.file_type, nvl(t.pretty_name, 'Unknown type') file_pretty_name, + f.db_type, nvl(d.pretty_db_name, 'All') as db_pretty_name + from apm_package_files f, apm_package_file_types t, apm_package_db_types d where f.version_id = :version_id and f.file_type = t.file_type_key(+) + and f.db_type = d.db_type_key(+) order by path } { incr counter @@ -78,7 +84,8 @@ doc_body_append "/" } else { doc_body_append "" - doc_body_append " $pretty_name " + doc_body_append " $file_pretty_name $db_pretty_name +  " if { $remove_files_p == 1 } { # Display a checkbox which the user can check to delete the file. doc_body_append "" Index: openacs-4/packages/acs-admin/www/apm/version-view.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-admin/www/apm/version-view.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-admin/www/apm/version-view.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-admin/www/apm/version-view.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -23,6 +23,15 @@ select count(*) n_files from apm_package_files where version_id = :version_id } +set supported_databases_list [db_list supported_databases { + select pretty_db_name + from apm_package_db_types + where exists (select 1 + from apm_package_files + where version_id = :version_id + and db_type = db_type_key) +}] + set downloaded_p [ad_decode $version_uri "" 0 1] # Obtain information about the enabled version of the package (if there is one). @@ -145,6 +154,19 @@ Version:$version_name Status:$status Data Model:$data_model_status +" + +if { [empty_string_p $supported_databases_list] } { + set supported_databases "none specified" +} else { + set supported_databases [join $supported_databases_list ", "] +} + +doc_body_append " +Database Support:$supported_databases +" + +doc_body_append " CVS:$cvs_status [ad_decode [llength $owners] 1 "Owner" "Owners"]:[join $owners "
"] Registered Files:$n_files Index: openacs-4/packages/acs-api-browser/acs-api-browser.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-api-browser/acs-api-browser.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-api-browser/acs-api-browser.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-api-browser/acs-api-browser.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Dennis Gregorovic A browser for Tcl and SQL APIs. 2001-03-06 Index: openacs-4/packages/acs-content-repository/acs-content-repository.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-content-repository/acs-content-repository.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-content-repository/acs-content-repository.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-content-repository/acs-content-repository.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Karl Goldstein A canonical repository for all ACS content. 2001-03-07 @@ -22,38 +23,65 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: openacs-4/packages/acs-content-repository/java/Regexp-oracle.sqlj =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-content-repository/java/Regexp-oracle.sqlj,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-content-repository/java/Regexp-oracle.sqlj 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,67 @@ +package com.arsdigita.content; + +import java.lang.reflect.*; +import java.sql.*; +import java.util.*; +import java.io.*; + +import oracle.sql.*; +import oracle.jdbc.driver.*; + +import sqlj.runtime.ref.DefaultContext; +import oracle.sqlj.runtime.Oracle; + +import com.oroinc.text.perl.*; + +public class Regexp { + + /** + Search for a pattern and replace it with another pattern + The patterns may be any valid Perl5 regular expressions + **/ + + public static boolean replace(int revisionID, String search, String replace) + throws SQLException, IOException, MalformedPerl5PatternException { + + Perl5Util util = new Perl5Util(); + String content; + + #sql { select blob_to_string(content) into :content from cr_revisions + where revision_id = :revisionID }; + + boolean hasMatch = util.match("/" + search + "/", content); + if (hasMatch) { + + String pattern = "s/" + search + "/" + replace + "/"; + + content = util.substitute(pattern, content); + Integer copyID; + BLOB blob; + + #sql { + begin + :OUT copyID := content_revision.copy(:revisionID); + end; + }; + + #sql { select content into :blob from cr_revisions + where revision_id = :copyID }; + + if (blob != null) + blob.putBytes(1, content.getBytes()); + } + + return hasMatch; + } +} + + + + + + + + + + + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-content-repository/java/Regexp.sqlj'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-content-repository/java/Util-oracle.sqlj =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-content-repository/java/Util-oracle.sqlj,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-content-repository/java/Util-oracle.sqlj 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,112 @@ +package com.arsdigita.content; + +// $Id: Util-oracle.sqlj,v 1.1 2001/04/05 18:23:38 donb Exp $ + +import java.sql.*; +import java.util.*; +import oracle.sql.*; +import java.io.*; + +public class Util { + + public static void stringToBlob(String s, oracle.sql.BLOB blob, int size) + throws SQLException { + + if (s == null) return; + + byte[] inBuffer = s.getBytes(); + + if (size < inBuffer.length) size = inBuffer.length; + + byte[] buffer = new byte[size]; + + System.arraycopy(inBuffer, 0, buffer, 0, inBuffer.length); + + blob.putBytes(1, buffer); + } + + public static void stringToBlob(String s, oracle.sql.BLOB blob) + throws SQLException { + + if (s == null) return; + + blob.putBytes(1, s.getBytes()); + } + + public static String blobToString(oracle.sql.BLOB blob) + throws SQLException { + + if (blob == null || blob.length() == 0) return ""; + + byte[] buffer = new byte[(int) blob.length()]; + + blob.getBytes(1, (int) blob.length(), buffer); + + String s = new String(buffer); + + return s; + } + + public static void blobToFile(String path, oracle.sql.BLOB blob) + throws SQLException { + + try { + + File aFile = new File(path); + FileOutputStream aFileOutputStream = new FileOutputStream(aFile); + long blobLength = blob.length(); + + aFile.mkdirs(); + + int chunkSize = blob.getChunkSize(); + byte[] buffer = new byte[chunkSize]; + for(long pos = 1; pos < blobLength; pos += chunkSize) { + chunkSize = blob.getBytes(pos, chunkSize, buffer); + aFileOutputStream.write(buffer, 0, chunkSize); + } + aFileOutputStream.close(); + } catch (IOException e) { + System.err.println("Error in writing " + path + ": " + e); + } + } + + public static void clobToBlob(oracle.sql.CLOB clob, oracle.sql.BLOB blob) + throws SQLException, IOException { + + if (clob == null) { + throw new SQLException("Received null value for clob argument."); + } + + if (blob == null) { + throw new SQLException("Received null value for blob argument."); + } + + OutputStream outstream = blob.getBinaryOutputStream(); + + // Get an input stream for the clob + Reader instream = clob.getCharacterStream(); + + int size = 4096; + char[] buffer = new char[size]; + int length = -1; + + while ((length = instream.read(buffer)) != -1) + outstream.write((new String(buffer)).getBytes(), 0, length); + instream.close(); + outstream.close(); + } + + // Write a BLOB to a CLOB, assuming the BLOB contains UTF-8 string + + public static void blobToClob(oracle.sql.BLOB blob, oracle.sql.CLOB clob) + throws SQLException, IOException { + + String s = blobToString(blob); + + // Get an output stream for the clob + Writer outstream = clob.getCharacterOutputStream(); + + outstream.write(s); + outstream.close(); + } +} Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-content-repository/java/Util.sqlj'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-content-repository/java/XMLExchange-oracle.sqlj =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-content-repository/java/XMLExchange-oracle.sqlj,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-content-repository/java/XMLExchange-oracle.sqlj 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,283 @@ +package com.arsdigita.content; + +import java.lang.reflect.*; +import java.sql.*; +import java.util.*; +import java.io.*; + +import oracle.sql.*; +import oracle.xml.parser.v2.*; +import oracle.jdbc.driver.*; + +import org.w3c.dom.*; +import sqlj.runtime.ref.DefaultContext; +import oracle.sqlj.runtime.Oracle; +import org.xml.sax.SAXException; + +#sql iterator TypeIter(String object_type, String table_name, + String id_column); +#sql iterator AttrIter(String attribute_name); + +public class XMLExchange { + + public static void main(String[] args) throws Exception { + + Integer revisionID = new Integer(args[0]); + PrintWriter out + = new PrintWriter( + new BufferedWriter(new OutputStreamWriter(System.out))); + + exportRevision(revisionID, out); + } + + public static int importRevision(Integer itemID, Integer revisionID, + CLOB loc) throws SQLException, IOException, + XMLParseException, SAXException { + + DOMParser parser = new DOMParser(); + + parser.parse(loc.getCharacterStream()); + XMLDocument doc = parser.getDocument(); + + doc.print(System.out); + + XMLElement revision = (XMLElement) doc.getDocumentElement(); + + // Create the revision + + String title = getChildText(revision, "title"); + String description = getChildText(revision, "description"); + String publishDate = getChildText(revision, "publish_date"); + String mimeType = getChildText(revision, "mime_type"); + String text = getChildText(revision, "text"); + + #sql { begin + :OUT revisionID := content_revision.new( + title => :title, + description => :description, + publish_date => to_date(:publishDate), + mime_type => :mimeType, + text => :text, + item_id => content_symlink.resolve(:itemID), + revision_id => :revisionID); + end; + }; + + // Query for additional tables in which to insert extended attributes + + TypeIter typeIter; + + #sql typeIter = { + select + object_type, table_name, id_column + from + acs_object_types + where + object_type ^= 'acs_object' + and + object_type ^= 'content_revision' + connect by + prior supertype = object_type + start with + object_type = ( + select object_type from acs_objects where object_id = :revisionID + ) + order by + level desc + }; + + String objectType; + String dmlColumns, dmlValues; + ArrayList attributes = new ArrayList(); + AttrIter attrIter; + + // Use JDBC for the actual insert rather than SQLJ because we need + // to build the DML dynamically + + Connection conn = DefaultContext.getDefaultContext().getConnection(); + + while (typeIter.next()) { + + objectType = typeIter.object_type(); + dmlColumns = "insert into " + typeIter.table_name() + "(" + + typeIter.id_column(); + dmlValues = ") values ( ?"; + + // query the attributes of the table + #sql attrIter = { + select + attribute_name + from + acs_attributes + where + object_type = :objectType + order by + attribute_name + }; + + while (attrIter.next()) { + dmlColumns += ", " + attrIter.attribute_name(); + dmlValues += ",?"; + attributes.add(attrIter.attribute_name()); + } + + PreparedStatement stmt = conn.prepareStatement(dmlColumns + + dmlValues + ")"); + + stmt.setInt(1, revisionID.intValue()); + for (int i = 0; i < attributes.size(); i++) { + stmt.setString(i + 2, + getChildText(revision, (String) attributes.get(i))); + } + + stmt.execute(); + stmt.close(); + attributes.clear(); + } + + return revisionID.intValue(); + } + + // Write XML to a CLOB + + public static int exportRevision(Integer revisionID, CLOB loc) + throws SQLException, IOException { + + PrintWriter out = new PrintWriter(loc.getCharacterOutputStream()); + exportRevision(revisionID, out); + + return revisionID.intValue(); + } + + // Default implementation of a function to write an XML + // representation of a content revision to an output stream. + + public static int exportRevision(Integer revisionID, PrintWriter out) + throws SQLException { + + try { + + XMLDocument doc = new XMLDocument(); + + // get the content type + + String contentType; + String tableName; + String isContentType; + #sql { + select + object_type, table_name, content_type.is_content_type(object_type) + into + :contentType, :tableName, :isContentType + from + acs_object_types + where + object_type = ( select object_type from acs_objects + where object_id = :revisionID ) + }; + + // screeen out non-content-types that are revisioned, such as templates + if (isContentType.equals("f")) return -1; + + XMLElement revision = new XMLElement(contentType); + doc.appendChild(revision); + + // select attributes for the revision + AttrIter attrIter; + + #sql attrIter = { + select + attribute_name + from + ( select + object_type, level sort_level + from + acs_object_types + where + object_type <> 'acs_object' + start with + object_type = :contentType + connect by + object_type = prior supertype + ) types, + acs_attributes attrs + where + attrs.object_type = types.object_type + order by + types.sort_level desc, attrs.sort_order + }; + + // build the query to select attributes from the view for the + // content type + String attrQuery = "select revision_id"; + + while (attrIter.next()) { + attrQuery += ", " + attrIter.attribute_name(); + } + + attrQuery += " from " + tableName + "x where revision_id = ?"; + + // select a row from the attribute view for the content type + Connection conn = DefaultContext.getDefaultContext().getConnection(); + + PreparedStatement stmt = conn.prepareStatement(attrQuery); + + stmt.setInt(1, revisionID.intValue()); + ResultSet rs = stmt.executeQuery(); + ResultSetMetaData md = rs.getMetaData(); + + if (rs.next()) { + for (int i = 1; i <= md.getColumnCount(); i++) { + // create an XML element for each attribute + String colName = md.getColumnName(i); + String colValue = rs.getString(i); + if (colValue == null) colValue = ""; + appendTextOnlyElement(revision, colName, colValue); + } + } + + stmt.close(); + doc.print(out); + + } catch (Exception e) { + throw new SQLException("Failed to generate XML document for revision " + + revisionID + ": " + e); + } + + return revisionID.intValue(); + } + + private static String getChildText(XMLElement element, String name) { + + NodeList nodes = element.getChildrenByTagName(name); + if (nodes.getLength() == 0) return null; + + // get the text node under this node + Node textNode = nodes.item(0).getFirstChild(); + + return textNode.getNodeValue(); + } + + private static void setChildText(XMLElement element, String text) { + + XMLText textNode = new XMLText(text); + element.appendChild(textNode); + } + + private static void appendTextOnlyElement(XMLElement parent, + String name, String text) { + XMLElement element = new XMLElement(name); + setChildText(element, text); + parent.appendChild(element); + } +} + + + + + + + + + + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-content-repository/java/XMLExchange.sqlj'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-core-docs/acs-core-docs.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/acs-core-docs.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-core-docs/acs-core-docs.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-core-docs/acs-core-docs.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Richard Li Documentation for the ACS Core. 2001-03-06 Index: openacs-4/packages/acs-kernel/acs-kernel.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-kernel/acs-kernel.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-kernel/acs-kernel.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-kernel/acs-kernel.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Routines and data models providing the foundation for ACS-based Web services. 2001-03-06 ArsDigita Corporation @@ -18,60 +19,108 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: openacs-4/packages/acs-kernel/sql/oracle/apm-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-kernel/sql/oracle/apm-create.sql,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-kernel/sql/oracle/apm-create.sql 20 Mar 2001 22:51:55 -0000 1.1 +++ openacs-4/packages/acs-kernel/sql/oracle/apm-create.sql 5 Apr 2001 18:23:38 -0000 1.2 @@ -508,22 +508,44 @@ insert into apm_package_file_types(file_type_key, pretty_name) values('documentation', 'Documentation'); insert into apm_package_file_types(file_type_key, pretty_name) values('tcl_procs', 'Tcl procedure library'); insert into apm_package_file_types(file_type_key, pretty_name) values('tcl_init', 'Tcl initialization'); + insert into apm_package_file_types(file_type_key, pretty_name) values('tcl_util', 'Tcl utility script'); insert into apm_package_file_types(file_type_key, pretty_name) values('content_page', 'Content page'); insert into apm_package_file_types(file_type_key, pretty_name) values('package_spec', 'Package specification'); insert into apm_package_file_types(file_type_key, pretty_name) values('data_model', 'Data model'); insert into apm_package_file_types(file_type_key, pretty_name) values('data_model_create', 'Data model installation'); insert into apm_package_file_types(file_type_key, pretty_name) values('data_model_drop', 'Data model deinstallation'); insert into apm_package_file_types(file_type_key, pretty_name) values('data_model_upgrade', 'Data model upgrade'); - insert into apm_package_file_types(file_type_key, pretty_name) values('java_code', 'Java Code'); + insert into apm_package_file_types(file_type_key, pretty_name) values('java_code', 'Java code'); + insert into apm_package_file_types(file_type_key, pretty_name) values('java_archive', 'Java archive'); + insert into apm_package_file_types(file_type_key, pretty_name) values('query_file', 'Query file'); insert into apm_package_file_types(file_type_key, pretty_name) values('template', 'Template file'); insert into apm_package_file_types(file_type_key, pretty_name) values('shell', 'Shell utility'); - insert into apm_package_file_types(file_type_key, pretty_name) values('sqlj_code', 'SQLJ Library'); + insert into apm_package_file_types(file_type_key, pretty_name) values('sqlj_code', 'SQLJ library'); commit; end; / show errors +create table apm_package_db_types ( + db_type_key varchar2(50) + constraint apm_package_db_types_pk primary key, + pretty_db_name varchar2(200) + constraint apm_package_db_types_name_nn not null +); + +comment on table apm_package_db_types is ' + A list of all the different kinds of database engines that an APM package can + support. This table is initialized in acs-tcl/tcl/apm-init.tcl rather than in + PL/SQL in order to guarantee that the list of supported database engines is + consistent between the bootstrap code and the package manager. +'; + -- Which files are contained in a version? + +-- Files may be constrained to work only with a single database engine. In practice only +-- datamodel and query files should be so constrained. If db_type is NULL the +-- file is loaded no matter which supported database engine is installed. + create table apm_package_files ( file_id integer constraint apm_package_files_id_pk primary key, @@ -533,10 +555,12 @@ path varchar2(1500) constraint apm_package_files_path_nn not null, file_type constraint apm_package_files_type_fk references apm_package_file_types, + db_type constraint apm_package_files_db_type_fk references apm_package_db_types, constraint apm_package_files_un unique(version_id, path) ); create bitmap index apm_pkg_files_file_type_idx on apm_package_files (file_type); +create bitmap index apm_pkg_files_db_type_idx on apm_package_files (db_type); comment on table apm_package_files is ' The files that belong to an APM package. We store this information in the database @@ -553,6 +577,9 @@ What kind of file is it? '; +comment on column apm_package_files.db_type is ' + If not null, which database engine does this file support? +'; create index apm_package_files_by_path on apm_package_files(path); create index apm_package_files_by_version on apm_package_files(version_id); @@ -1121,7 +1148,9 @@ default null, version_id in apm_package_versions.version_id%TYPE, path in apm_package_files.path%TYPE, - file_type in apm_package_file_types.file_type_key%TYPE + file_type in apm_package_file_types.file_type_key%TYPE, + db_type in apm_package_db_types.db_type_key%TYPE + default null ) return apm_package_files.file_id%TYPE; -- Remove a file from the indicated version. @@ -2090,7 +2119,9 @@ default null, version_id in apm_package_versions.version_id%TYPE, path in apm_package_files.path%TYPE, - file_type in apm_package_file_types.file_type_key%TYPE + file_type in apm_package_file_types.file_type_key%TYPE, + db_type in apm_package_db_types.db_type_key%TYPE + default null ) return apm_package_files.file_id%TYPE is v_file_id apm_package_files.file_id%TYPE; @@ -2110,9 +2141,10 @@ end if; insert into apm_package_files - (file_id, version_id, path, file_type) + (file_id, version_id, path, file_type, db_type) values - (v_file_id, add_file.version_id, add_file.path, add_file.file_type); + (v_file_id, add_file.version_id, add_file.path, add_file.file_type, + add_file.db_type); return v_file_id; end add_file; Index: openacs-4/packages/acs-kernel/sql/postgresql/apm-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-kernel/sql/postgresql/apm-create.sql,v diff -u -r1.5 -r1.6 --- openacs-4/packages/acs-kernel/sql/postgresql/apm-create.sql 3 Apr 2001 04:28:59 -0000 1.5 +++ openacs-4/packages/acs-kernel/sql/postgresql/apm-create.sql 5 Apr 2001 18:23:38 -0000 1.6 @@ -677,16 +677,19 @@ insert into apm_package_file_types(file_type_key, pretty_name) values(''documentation'', ''Documentation''); insert into apm_package_file_types(file_type_key, pretty_name) values(''tcl_procs'', ''Tcl procedure library''); insert into apm_package_file_types(file_type_key, pretty_name) values(''tcl_init'', ''Tcl initialization''); + insert into apm_package_file_types(file_type_key, pretty_name) values(''tcl_util'', ''Tcl utility script''); insert into apm_package_file_types(file_type_key, pretty_name) values(''content_page'', ''Content page''); insert into apm_package_file_types(file_type_key, pretty_name) values(''package_spec'', ''Package specification''); insert into apm_package_file_types(file_type_key, pretty_name) values(''data_model'', ''Data model''); insert into apm_package_file_types(file_type_key, pretty_name) values(''data_model_create'', ''Data model installation''); insert into apm_package_file_types(file_type_key, pretty_name) values(''data_model_drop'', ''Data model deinstallation''); insert into apm_package_file_types(file_type_key, pretty_name) values(''data_model_upgrade'', ''Data model upgrade''); - insert into apm_package_file_types(file_type_key, pretty_name) values(''java_code'', ''Java Code''); + insert into apm_package_file_types(file_type_key, pretty_name) values(''java_code'', ''Java code''); + insert into apm_package_file_types(file_type_key, pretty_name) values(''java_archive'', ''Java archive''); + insert into apm_package_file_types(file_type_key, pretty_name) values(''query_file'', ''Query file''); insert into apm_package_file_types(file_type_key, pretty_name) values(''template'', ''Template file''); insert into apm_package_file_types(file_type_key, pretty_name) values(''shell'', ''Shell utility''); - insert into apm_package_file_types(file_type_key, pretty_name) values(''sqlj_code'', ''SQLJ Library''); + insert into apm_package_file_types(file_type_key, pretty_name) values(''sqlj_code'', ''SQLJ library''); return 0; end;' language 'plpgsql'; @@ -695,10 +698,26 @@ drop function inline_3 (); +create table apm_package_db_types ( + db_type_key varchar(50) + constraint apm_package_db_types_pk primary key, + pretty_db_name varchar(200) + constraint apm_package_db_types_name_nn not null +); --- show errors +comment on table apm_package_db_types is ' + A list of all the different kinds of database engines that an APM package can + support. This table is initialized in acs-tcl/tcl/apm-init.tcl rather than in + PL/SQL in order to guarantee that the list of supported database engines is + consistent between the bootstrap code and the package manager. +'; -- Which files are contained in a version? + +-- Files may be constrained to work only with a single database engine. In practice only +-- datamodel and query files should be so constrained. If file_database_type is NULL the +-- file is loaded no matter which supported database engine is installed. + create table apm_package_files ( file_id integer constraint apm_package_files_id_pk primary key, @@ -708,10 +727,12 @@ path varchar(1500) constraint apm_package_files_path_nn not null, file_type varchar(50) constraint apm_package_files_type_fk references apm_package_file_types, + db_type varchar(50) constraint apm_package_files_db_type_fk references apm_package_db_types, constraint apm_package_files_un unique(version_id, path) ); create index apm_pkg_files_file_type_idx on apm_package_files (file_type); +create index apm_pkg_files_db_type_idx on apm_package_files (db_type); comment on table apm_package_files is ' The files that belong to an APM package. We store this information in the database @@ -728,10 +749,10 @@ What kind of file is it? '; +comment on column apm_package_files.db_type is ' + If not null, which database engine does this file support? +'; -create index apm_package_files_by_path on apm_package_files(path); -create index apm_package_files_by_version on apm_package_files(version_id); - -- A useful view for combining the package information with the file information. create view apm_file_info as @@ -2439,13 +2460,14 @@ -- function add_file -create function apm_package_version__add_file (integer,integer,varchar,varchar) +create function apm_package_version__add_file (integer,integer,varchar,varchar, varchar) returns integer as ' declare add_file__file_id alias for $1; add_file__version_id alias for $2; add_file__path alias for $3; add_file__file_type alias for $4; + add_file__db_type alias for $5; v_file_id apm_package_files.file_id%TYPE; v_file_exists_p integer; begin @@ -2462,9 +2484,9 @@ end if; insert into apm_package_files - (file_id, version_id, path, file_type) + (file_id, version_id, path, file_type, db_type) values - (v_file_id, add_file__version_id, add_file__path, add_file__file_type); + (v_file_id, add_file__version_id, add_file__path, add_file__file_type, add_file__db_type); end if; return v_file_id; Index: openacs-4/packages/acs-mail/acs-mail.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-mail/acs-mail.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-mail/acs-mail.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-mail/acs-mail.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + John Prevost General messaging system, mark II 2001-03-06 @@ -24,9 +25,9 @@ - - - + + + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-mail/sql/acs-mail-create-packages.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-mail/sql/acs-mail-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-mail/sql/acs-mail-queue-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-mail/sql/oracle/acs-mail-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-mail/sql/oracle/acs-mail-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-mail/sql/oracle/acs-mail-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,255 @@ +-- +-- packages/acs-mail/sql/acs-mail-create.sql +-- +-- @author John Prevost +-- @creation-date 2001-01-08 +-- @cvs-id $Id: acs-mail-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +-- Typical usage when creating: + +-- For text only +-- +-- body_id := acs_mail_body.new ( ... ); +-- text_id := acs_mail_object.new ( ... ); +-- acs_content.set_parameters ( text_id, ... ); +-- update acs_content set content = empty_blob() where object_id = text_id; +-- acs_mail_body.set_content(text_id); +-- bboard_message.new ( ..., body_id ); + +-- tcl possibilities: +-- set body_id [acs_mail_body_new ...] +-- set text_id [acs_mail_object_new ... -content $text ] +-- acs_mail_body_set_content $body_id $text_id +-- set msg_id [bboard_message_new ... $body_id] +-- *or* +-- set body_id [acs_mail_body_new ... -content $text] +-- set msg_id [bboard_message_new ... $body_id] + +-- For attachments (multipart/mixed) +-- +-- body_id := acs_mail_body.new ( ... ); +-- part_id := acs_mail_multipart.new ( ..., 'multipart/mixed' ); +-- text_id := acs_mail_object.new ( ... ); +-- { ... content stuff ... } +-- photo_id := acs_mail_object.new ( ... ); +-- { ... content stuff ... } +-- acs_mail_multipart.add_content ( part_id, text_id ); +-- acs_mail_multipart.add_content ( part_id, photo_id ); +-- acs_mail_body.set_content ( part_id ); +-- bboard_message.new ( ..., body_id ); + +-- For alternatives +-- (Same as above, but 'multipart/alternative' instead of 'multipart/mixed') + +-- Typical usage when displaying: + +-- select ... from ... (tree query) +-- 0 RFC822 Header (ignored) +-- 1 multipart/mixed (attachments!) +-- 1.1 text/plain (spit it out) +-- 1.2 image/gif (inline it) +-- 1.3 text/plain (more text to spit out +-- 1.4 message/rfc822 +-- 1.4.0 header of submessage + +-- 0000 (not sure about the numbering stuff yet) is always the RFC822 +-- header, autogenerated or from incoming email. +-- 0001 will always be the content of the message +-- within 0001 may be more items, depending on the structure of the message. + +-- Common headers are also available decomposed into useful forms. + +-- Exactly how incoming messages get transformed into this structure +-- and how outgoing messages get transformed from this structure is to +-- be implemented soon. + +set feedback off + +-- Object System Metadata ---------------------------------------------- + +-- A messaging object, which is subject to garbage collection by the +-- messaging system. If any object in this table is not found in + +-- select body_id as o_id from acs_mail_links +-- union +-- select object_id as o_id from acs_mail_multipart_parts +-- union +-- select body_content as o_id from acs_mail_bodies + +-- then it is removed. It is assumed that an object cannot satisfy +-- the above predicate if it ever stops satisfying it (outside of a +-- transaction.) + +begin + acs_object_type.create_type ( + supertype => 'acs_object', + object_type => 'acs_mail_gc_object', + pretty_name => 'ACS Messaging Object', + pretty_plural => 'ACS Messaging Objects', + table_name => 'ACS_MAIL_GC_OBJECTS', + id_column => 'OBJECT_ID', + package_name => 'ACS_MAIL_GC_OBJECT', + name_method => 'ACS_OBJECT.DEFAULT_NAME' + ); +end; +/ +show errors + +-- Mail bodies are automatically GC'd. These contain the data +-- relevant to a single message. These are shared by being pointed to +-- by many acs_mail_links. This should not be subtyped. + +begin + acs_object_type.create_type ( + supertype => 'acs_mail_gc_object', + object_type => 'acs_mail_body', + pretty_name => 'Mail Body', + pretty_plural => 'Mail Bodies', + table_name => 'ACS_MAIL_BODIES', + id_column => 'BODY_ID', + package_name => 'ACS_MAIL_BODY', + name_method => 'ACS_OBJECT.DEFAULT_NAME' + ); +end; +/ +show errors + +-- multipart mime parts are automatically GC'd. These contain +-- multiple parts to make up alternatives or mixed content +-- (attachments). These may be shared by belonging to multiple +-- mail_links. + +begin + acs_object_type.create_type ( + supertype => 'acs_mail_gc_object', + object_type => 'acs_mail_multipart', + pretty_name => 'ACS Mail Multipart Object', + pretty_plural => 'ACS Mail Multipart Objects', + table_name => 'ACS_MAIL_MULTIPARTS', + id_column => 'MULTIPART_ID', + package_name => 'ACS_MAIL_MULTIPART', + name_method => 'ACS_OBJECT.DEFAULT_NAME' + ); +end; +/ +show errors + +-- A mail_link, subtypable, and used by applications to track messages. +-- Permissions should be set at this level. These should not be +-- shared between applications: rather, an application should create a +-- new mail_link and use it as it wills. When it's done, it should +-- delete this, which will cause the other objects to be garbage +-- collected. + +begin + acs_object_type.create_type ( + supertype => 'acs_object', + object_type => 'acs_mail_link', + pretty_name => 'Mail Message', + pretty_plural => 'Mail Messages', + table_name => 'ACS_MAIL_LINKS', + id_column => 'MAIL_LINK_ID', + package_name => 'ACS_MAIL_LINK', + name_method => 'ACS_OBJECT.DEFAULT_NAME' + ); + +end; +/ +show errors + +-- Raw Tables and Comments --------------------------------------------- + + -- All garbage collectable objects are in this table + +create table acs_mail_gc_objects ( + gc_object_id integer + constraint acs_mail_gc_objs_object_id_pk + primary key + constraint acs_mail_gc_objs_object_id_fk + references acs_objects +); + + -- Mail bodies + +create table acs_mail_bodies ( + body_id integer + constraint acs_mail_bodies_body_id_pk primary key + constraint acs_mail_bodies_body_id_fk + references acs_mail_gc_objects on delete cascade, + body_reply_to integer + constraint acs_mail_bodies_reply_to_fk + references acs_mail_bodies on delete set null, + body_from integer + constraint acs_mail_bodies_body_from_fk + references parties on delete set null, + body_date date, + header_message_id varchar2(1000) + constraint acs_mail_bodies_h_m_id_un unique + constraint acs_mail_bodies_h_m_id_nn not null, + header_reply_to varchar2(1000), + header_subject varchar2(4000), + header_from varchar2(4000), + header_to varchar2(4000), + content_object_id integer + constraint acs_mail_bodies_content_oid_fk + references acs_objects +); + +create table acs_mail_body_headers ( + body_id integer + constraint acs_mail_body_heads_body_id_fk + references acs_mail_bodies on delete cascade, + header_name varchar2(1000), + header_content varchar2(4000) +); + +create index acs_mail_body_hdrs_body_id_idx + on acs_mail_body_headers (body_id); + + -- MIME Multiparts + +create table acs_mail_multiparts ( + multipart_id integer + constraint acs_mail_multiparts_mp_id_pk primary key + constraint acs_mail_multiparts_mp_id_fk + references acs_mail_gc_objects on delete cascade, + multipart_kind varchar2(120) + constraint acs_mail_multiparts_mp_kind_nn not null +); + +create table acs_mail_multipart_parts ( + multipart_id integer + constraint acs_mail_mp_parts_mp_id_fk + references acs_mail_multiparts + on delete cascade, + mime_filename varchar2(1000), + mime_disposition varchar2(1000), + sequence_number integer, + content_object_id integer + constraint acs_mail_mp_parts_c_obj_id_fk references acs_objects, + constraint acs_mail_multipart_parts_pk + primary key (multipart_id, sequence_number) +); + + -- Mail Links + +create table acs_mail_links ( + mail_link_id integer + constraint acs_mail_links_ml_id_pk primary key + constraint acs_mail_links_ml_id_fk references acs_objects, + body_id integer + constraint acs_mail_links_body_id_nn not null + constraint acs_mail_links_body_id_fk references acs_mail_bodies +); + + +-- API ----------------------------------------------------------------- + +-- APIs for the datamodel in this file, separated out for future upgrades +@@ acs-mail-packages-create + +-- Supporting Datamodels ----------------------------------------------- + +-- The mail queue datamodel +@@ acs-mail-queue-create Index: openacs-4/packages/acs-mail/sql/oracle/acs-mail-packages-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-mail/sql/oracle/acs-mail-packages-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-mail/sql/oracle/acs-mail-packages-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,460 @@ +-- +-- packages/acs-mail/sql/acs-mail-create-packages.sql +-- +-- @author John Prevost +-- @creation-date 2001-01-08 +-- @cvs-id $Id: acs-mail-packages-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +-- Package Interfaces -------------------------------------------------- + +create or replace package acs_mail_gc_object +as + + function new ( + gc_object_id in acs_objects.object_id%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_gc_object', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE; + + procedure delete ( + gc_object_id in acs_mail_gc_objects.gc_object_id%TYPE + ); + +end; +/ +show errors + +create or replace package acs_mail_body +as + + function new ( + body_id in acs_mail_bodies.body_id%TYPE default null, + body_reply_to in acs_mail_bodies.body_reply_to%TYPE default null, + body_from in acs_mail_bodies.body_from%TYPE default null, + body_date in acs_mail_bodies.body_date%TYPE default null, + header_message_id in acs_mail_bodies.header_message_id%TYPE default null, + header_reply_to in acs_mail_bodies.header_reply_to%TYPE default null, + header_subject in acs_mail_bodies.header_subject%TYPE default null, + header_from in acs_mail_bodies.header_from%TYPE default null, + header_to in acs_mail_bodies.header_to%TYPE default null, + content_object_id in acs_mail_bodies.content_object_id%TYPE default null, + + object_type in acs_objects.object_type%TYPE default 'acs_mail_body', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE; + + procedure delete ( + body_id in acs_mail_bodies.body_id%TYPE + ); + + function body_p ( + object_id in acs_objects.object_id%TYPE + ) return char; + + -- duplicate a mail body to make changes safely + + function clone ( + old_body_id in acs_mail_bodies.body_id%TYPE, + body_id in acs_mail_bodies.body_id%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_body', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_user%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE; + + -- set the main content object of a mail body + + procedure set_content_object ( + body_id in acs_mail_bodies.body_id%TYPE, + content_object_id in acs_mail_bodies.content_object_id%TYPE + ); + +end; +/ +show errors + +create or replace package acs_mail_multipart +as + + function new ( + multipart_id in acs_mail_multiparts.multipart_id%TYPE default null, + multipart_kind in acs_mail_multiparts.multipart_kind%TYPE, + + object_type in acs_objects.object_type%TYPE + default 'acs_mail_multipart', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE; + + procedure delete ( + multipart_id in acs_mail_multiparts.multipart_id%TYPE + ); + + function multipart_p ( + object_id in acs_objects.object_id%TYPE + ) return char; + + -- Add content at a specific index. If the sequence number is null, + -- below one, or higher than the highest item already available, + -- adds at the end. Otherwise, inserts and renumbers others. + + procedure add_content ( + multipart_id in acs_mail_multipart_parts.multipart_id%TYPE, + content_object_id in acs_mail_multipart_parts.content_object_id%TYPE + ); + +end acs_mail_multipart; +/ +show errors + +create or replace package acs_mail_link +as + + function new ( + mail_link_id in acs_mail_links.mail_link_id%TYPE default null, + body_id in acs_mail_bodies.body_id%TYPE, + + context_id in acs_objects.context_id%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_link' + ) return acs_objects.object_id%TYPE; + + procedure delete ( + mail_link_id in acs_mail_links.mail_link_id%TYPE + ); + + function link_p ( + object_id in acs_objects.object_id%TYPE + ) return char; + +end acs_mail_link; +/ +show errors + +-- Package Implementations --------------------------------------------- + +create or replace package body acs_mail_gc_object +as + function new ( + gc_object_id in acs_objects.object_id%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_gc_object', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE + is + v_object_id acs_objects.object_id%TYPE; + begin + v_object_id := acs_object.new ( + object_id => gc_object_id, + object_type => object_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + context_id => context_id + ); + insert into acs_mail_gc_objects values ( v_object_id ); + return v_object_id; + end new; + + procedure delete ( + gc_object_id in acs_mail_gc_objects.gc_object_id%TYPE + ) + is + begin + delete from acs_mail_gc_objects + where gc_object_id = acs_mail_gc_object.delete.gc_object_id; + acs_object.delete(gc_object_id); + end delete; + +end acs_mail_gc_object; +/ +show errors + +create or replace package body acs_mail_body +as + + function new ( + body_id in acs_mail_bodies.body_id%TYPE default null, + body_reply_to in acs_mail_bodies.body_reply_to%TYPE default null, + body_from in acs_mail_bodies.body_from%TYPE default null, + body_date in acs_mail_bodies.body_date%TYPE default null, + header_message_id in acs_mail_bodies.header_message_id%TYPE default null, + header_reply_to in acs_mail_bodies.header_reply_to%TYPE default null, + header_subject in acs_mail_bodies.header_subject%TYPE default null, + header_from in acs_mail_bodies.header_from%TYPE default null, + header_to in acs_mail_bodies.header_to%TYPE default null, + content_object_id in acs_mail_bodies.content_object_id%TYPE default null, + + object_type in acs_objects.object_type%TYPE default 'acs_mail_body', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE + is + v_object_id acs_objects.object_id%TYPE; + v_header_message_id acs_mail_bodies.header_message_id%TYPE; + begin + v_object_id := acs_mail_gc_object.new ( + gc_object_id => body_id, + object_type => object_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + context_id => context_id + ); + v_header_message_id := + nvl(header_message_id, + sysdate || '.' || v_object_id || '@' || + utl_inaddr.get_host_name || '.sddd'); + insert into acs_mail_bodies + (body_id, body_reply_to, body_from, body_date, header_message_id, + header_reply_to, header_subject, header_from, header_to, + content_object_id) + values + (v_object_id, body_reply_to, body_from, body_date, + v_header_message_id, header_reply_to, header_subject, header_from, + header_to, content_object_id); + return v_object_id; + end new; + + procedure delete ( + body_id in acs_mail_bodies.body_id%TYPE + ) + is + begin + acs_mail_gc_object.delete(body_id); + end delete; + + function body_p ( + object_id in acs_objects.object_id%TYPE + ) return char + is + v_check_body_id integer; + begin + select decode(count(body_id),0,0,1) into v_check_body_id + from acs_mail_bodies + where body_id = object_id; + if v_check_body_id <> 0 then + return 't'; + else + return 'f'; + end if; + end body_p; + + function clone ( + old_body_id in acs_mail_bodies.body_id%TYPE, + body_id in acs_mail_bodies.body_id%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_body', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_user%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE + is + v_object_id acs_objects.object_id%TYPE; + body_reply_to acs_mail_bodies.body_reply_to%TYPE; + body_from acs_mail_bodies.body_from%TYPE; + body_date acs_mail_bodies.body_date%TYPE; + header_message_id acs_mail_bodies.header_message_id%TYPE; + header_reply_to acs_mail_bodies.header_reply_to%TYPE; + header_subject acs_mail_bodies.header_subject%TYPE; + header_from acs_mail_bodies.header_from%TYPE; + header_to acs_mail_bodies.header_to%TYPE; + content_object_id acs_mail_bodies.content_object_id%TYPE; + begin + select body_reply_to, body_from, body_date, + header_reply_to, header_subject, header_from, header_to, + content_object_id + into body_reply_to, body_from, body_date, + header_reply_to, header_subject, header_from, header_to, + content_object_id + from acs_mail_bodies + where body_id = old_body_id; + v_object_id := acs_mail_body.new ( + body_id => body_id, + body_reply_to => body_reply_to, + body_from => body_from, + body_date => body_date, + header_reply_to => header_reply_to, + header_subject => header_subject, + header_from => header_from, + header_to => header_to, + content_object_id => content_object_id, + object_type => object_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + context_id => context_id + ); + return v_object_id; + end clone; + + procedure set_content_object ( + body_id in acs_mail_bodies.body_id%TYPE, + content_object_id in acs_mail_bodies.content_object_id%TYPE + ) + is + begin + update acs_mail_bodies + set content_object_id = set_content_object.content_object_id + where body_id = set_content_object.body_id; + end set_content_object; + +end acs_mail_body; +/ +show errors + +create or replace package body acs_mail_multipart +as + + function new ( + multipart_id in acs_mail_multiparts.multipart_id%TYPE default null, + multipart_kind in acs_mail_multiparts.multipart_kind%TYPE, + + object_type in acs_objects.object_type%TYPE + default 'acs_mail_multipart', + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + context_id in acs_objects.context_id%TYPE default null + ) return acs_objects.object_id%TYPE + is + v_object_id acs_objects.object_id%TYPE; + begin + v_object_id := acs_mail_gc_object.new ( + gc_object_id => multipart_id, + object_type => object_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + context_id => context_id + ); + insert into acs_mail_multiparts (multipart_id, multipart_kind) + values (v_object_id, multipart_kind); + return v_object_id; + end new; + + procedure delete ( + multipart_id in acs_mail_multiparts.multipart_id%TYPE + ) + is + begin + acs_mail_gc_object.delete(multipart_id); + end delete; + + function multipart_p ( + object_id in acs_objects.object_id%TYPE + ) return char + is + v_check_multipart_id integer; + begin + select decode(count(multipart_id),0,0,1) into v_check_multipart_id + from acs_mail_multiparts + where multipart_id = object_id; + if v_check_multipart_id <> 0 then + return 't'; + else + return 'f'; + end if; + end multipart_p; + + -- Add content at a specific index. If the sequence number is null, + -- below one, or higher than the highest item already available, + -- adds at the end. Otherwise, inserts and renumbers others. + + procedure add_content ( + multipart_id in acs_mail_multipart_parts.multipart_id%TYPE, + content_object_id in acs_mail_multipart_parts.content_object_id%TYPE + ) is + v_multipart_id acs_mail_multiparts.multipart_id%TYPE; + v_max_num integer; + begin + -- get a row lock on the multipart item + select multipart_id into v_multipart_id from acs_mail_multiparts + where multipart_id = add_content.multipart_id for update; + select nvl(max(sequence_number),0) into v_max_num + from acs_mail_multipart_parts + where multipart_id = add_content.multipart_id; + insert into acs_mail_multipart_parts + (multipart_id, sequence_number, content_object_id) + values + (multipart_id, v_max_num + 1, content_object_id); + end add_content; + +end acs_mail_multipart; +/ +show errors + +create or replace package body acs_mail_link +as + + function new ( + mail_link_id in acs_mail_links.mail_link_id%TYPE default null, + body_id in acs_mail_bodies.body_id%TYPE, + + context_id in acs_objects.context_id%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_link' + ) return acs_objects.object_id%TYPE + is + v_object_id acs_objects.object_id%TYPE; + begin + v_object_id := acs_object.new ( + object_id => mail_link_id, + context_id => context_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + object_type => object_type + ); + insert into acs_mail_links ( mail_link_id, body_id ) + values ( v_object_id, body_id ); + return v_object_id; + end; + + procedure delete ( + mail_link_id in acs_mail_links.mail_link_id%TYPE + ) + is + begin + delete from acs_mail_links + where mail_link_id = acs_mail_link.delete.mail_link_id; + acs_object.delete(mail_link_id); + end; + + function link_p ( + object_id in acs_objects.object_id%TYPE + ) return char + is + v_check_link_id integer; + begin + select decode(count(mail_link_id),0,0,1) into v_check_link_id + from acs_mail_links + where mail_link_id = object_id; + if v_check_link_id <> 0 then + return 't'; + else + return 'f'; + end if; + end link_p; + +end acs_mail_link; +/ +show errors Index: openacs-4/packages/acs-mail/sql/oracle/acs-mail-queue-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-mail/sql/oracle/acs-mail-queue-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-mail/sql/oracle/acs-mail-queue-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,120 @@ +-- +-- packages/acs-mail/sql/acs-mail-queue-create.sql +-- +-- @author John Prevost +-- @creation-date 2001-01-08 +-- @cvs-id $Id: acs-mail-queue-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +begin + acs_object_type.create_type ( + supertype => 'acs_mail_link', + object_type => 'acs_mail_queue_message', + pretty_name => 'Queued Message', + pretty_plural => 'Queued Messages', + table_name => 'ACS_MESSAGES_QUEUE_MESSAGE', + id_column => 'MESSAGE_ID', + name_method => 'ACS_OBJECT.DEFAULT_NAME' + ); +end; +/ +show errors + +create table acs_mail_queue_messages ( + message_id integer + constraint acs_mail_queue_ml_id_pk primary key + constraint acs_mail_queue_ml_id_fk references acs_mail_links +); + +create table acs_mail_queue_incoming ( + message_id integer + constraint acs_mail_queue_in_mlid_pk primary key + constraint acs_mail_queue_in_mlid_fk + references acs_mail_queue_messages, + envelope_from varchar2(4000), + envelope_to varchar2(4000) +); + +create table acs_mail_queue_outgoing ( + message_id integer + constraint acs_mail_queue_out_mlid_pk primary key + constraint acs_mail_queue_out_mlid_fk + references acs_mail_queue_messages, + envelope_from varchar2(4000), + envelope_to varchar2(4000) +); + +-- API ----------------------------------------------------------------- + +create or replace package acs_mail_queue_message +as + + function new ( + mail_link_id in acs_mail_links.mail_link_id%TYPE default null, + body_id in acs_mail_bodies.body_id%TYPE, + + context_id in acs_objects.context_id%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_link' + ) return acs_objects.object_id%TYPE; + + procedure delete ( + message_id in acs_mail_links.mail_link_id%TYPE + ); +end acs_mail_queue_message; +/ +show errors + +create or replace package body acs_mail_queue_message +as + + function new ( + mail_link_id in acs_mail_links.mail_link_id%TYPE default null, + body_id in acs_mail_bodies.body_id%TYPE, + + context_id in acs_objects.context_id%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_mail_link' + ) return acs_objects.object_id%TYPE + is + v_object_id acs_objects.object_id%TYPE; + begin + v_object_id := acs_mail_link.new ( + mail_link_id => mail_link_id, + body_id => body_id, + context_id => context_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + object_type => object_type + ); + insert into acs_mail_queue_messages ( message_id ) + values ( v_object_id ); + return v_object_id; + end; + + procedure delete ( + message_id in acs_mail_links.mail_link_id%TYPE + ) + is + begin + delete from acs_mail_queue_messages + where message_id = acs_mail_queue_message.delete.message_id; + acs_mail_link.delete(message_id); + end; + +end acs_mail_queue_message; +/ +show errors + + +-- Needs: +-- Incoming: +-- A way to say "okay, I've accepted this one, go ahead and delete" +-- Outgoing: +-- A way to say "send this message to this person from this person" +-- A way to say "send this message to these people from this person" Index: openacs-4/packages/acs-messaging/acs-messaging.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/acs-messaging.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-messaging/acs-messaging.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-messaging/acs-messaging.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + John Prevost Anukul Kapoor General messaging for bboard, general comments, etc. @@ -23,14 +24,14 @@ - - - - - - - - + + + + + + + + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-messaging/sql/acs-messaging-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-messaging/sql/acs-messaging-drop.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-messaging/sql/acs-messaging-packages.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-messaging/sql/acs-messaging-views.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,138 @@ +-- +-- packages/acs-messaging/sql/acs-messaging-create.sql +-- +-- @author John Prevost +-- @creation-date 2000-08-27 +-- @cvs-id $Id: acs-messaging-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +set feedback off + +-- Object System Metadata ---------------------------------------------- + +begin + + acs_object_type.create_type ( + supertype => 'content_item', + object_type => 'acs_message', + pretty_name => 'Message', + pretty_plural => 'Messages', + table_name => 'ACS_MESSAGES', + id_column => 'MESSAGE_ID', + name_method => 'ACS_MESSAGE.NAME' + ); + + acs_object_type.create_type ( + supertype => 'content_revision', + object_type => 'acs_message_revision', + pretty_name => 'Message Revision', + pretty_plural => 'Message Revisions', + table_name => 'CR_REVISIONS', + id_column => 'REVISION_ID', + name_method => 'ACS_OBJECT.DEFAULT_NAME' + ); + +end; +/ +show errors + +-- Raw Tables and Comments --------------------------------------------- + +create table acs_messages ( -- extends cr_items + message_id integer + constraint acs_messages_message_id_fk + references cr_items (item_id) on delete cascade + constraint acs_messages_pk + primary key, + -- we will need to find a way to make reply_to go to 0 instead of null + -- to improve scalability + reply_to integer + constraint acs_messages_reply_to_fk + references acs_messages (message_id) on delete set null, + sent_date date + constraint acs_messages_sent_date_nn + not null, + sender integer + constraint acs_messages_sender_fk + references parties (party_id), + rfc822_id varchar2(250) + constraint acs_messages_rfc822_id_nn + not null + constraint acs_messages_rfc822_id_un + unique +); + +create index acs_messages_reply_to_idx on acs_messages (reply_to); +create index acs_messages_sender_idx on acs_messages (sender); + +comment on table acs_messages is ' + A generic message which may be attached to any object in the system. +'; + +comment on column acs_messages.reply_to is ' + Pointer to a message this message contains a reply to, for threading. +'; + +comment on column acs_messages.sent_date is ' + The date the message was sent (may be distinct from when it was created + or published in the system.) +'; + +comment on column acs_messages.sender is ' + The person who sent the message (may be distinct from the person who + entered the message in the system.) +'; + +comment on column acs_messages.rfc822_id is ' + The RFC822 message-id of this message, for sending email. +'; + +create table acs_messages_outgoing ( + message_id integer + constraint amo_message_id_fk + references acs_messages (message_id) on delete cascade, + to_address varchar2(1000) + constraint amo_to_address_nn + not null, + grouping_id integer, + wait_until date + constraint amo_wait_until_nn not null, + constraint acs_messages_outgoing_pk + primary key (message_id, to_address) +); + +comment on table acs_messages_outgoing is ' + Set of messages to be sent to parties. It is assumed that sending a + message either queues it in a real MTA or fails, so no information about + what''s been tried how many times is kept. +'; + +comment on column acs_messages_outgoing.to_address is ' + The email address to send this message to. Note that this will + probably become a party_id again once upgrading a party to a user + is possible. +'; + +comment on column acs_messages_outgoing.grouping_id is ' + This identifier is used to group sets of messages to be sent as + digests. When a message is about to be sent, any other messages + with the same grouping_id will be put together with it in a + digest. It is recommended but not required that an object id is + used. Bboard, for example, might use the forum id that the user''s + subscribed to. For instant (non-digest) updates, it would be + appropriate to use null, which is never equal to anything else. +'; + +comment on column acs_messages_outgoing.wait_until is ' + Don''t schedule a send until after this date. If another message with + the same grouping ID is scheduled to be sent, then this message may be + sent at the same time. (So, for example, daily digests would be + achieved by setting the grouping_id to the same value, and the wait_until + value to the end of the current day. As soon as one message in the group + is to be sent, all will be sent.) +'; + +@@ acs-messaging-views +@@ acs-messaging-packages + +set feedback on Index: openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,22 @@ +-- +-- packages/acs-messaging/sql/acs-messaging-drop.sql +-- +-- @author akk@arsdigita.com +-- @creation-date 2000-08-31 +-- @cvs-id $Id: acs-messaging-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +begin + acs_object_type.drop_type('acs_message'); +end; +/ +show errors + +drop package acs_message; + +drop table acs_messages_outgoing; + +drop view acs_messages_all; + +drop table acs_messages; + Index: openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-packages.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-packages.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-packages.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,637 @@ +-- +-- packages/acs-messaging/sql/acs-messaging-packages.sql +-- +-- @author John Prevost +-- @author Phong Nguyen +-- @creation-date 2000-08-27 +-- @cvs-id $Id: acs-messaging-packages.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +create or replace package acs_message +as + + function new ( + message_id in acs_messages.message_id%TYPE default null, + reply_to in acs_messages.reply_to%TYPE default null, + sent_date in acs_messages.sent_date%TYPE default sysdate, + sender in acs_messages.sender%TYPE default null, + rfc822_id in acs_messages.rfc822_id%TYPE default null, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + parent_id in cr_items.parent_id%TYPE default 0, + context_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_message', + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + function edit ( + message_id in acs_messages.message_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + procedure delete ( + message_id in acs_messages.message_id%TYPE + ); + + function message_p ( + message_id in acs_messages.message_id%TYPE + ) return char; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + recipient_id in parties.party_id%TYPE, + grouping_id in integer default null, + wait_until in date default sysdate + ); + + procedure send ( + message_id in acs_messages.message_id%TYPE, + to_address in varchar2, + grouping_id in integer default null, + wait_until in date default sysdate + ); + + function first_ancestor ( + message_id in acs_messages.message_id%TYPE + ) return acs_messages.message_id%TYPE; + + -- ACHTUNG! WARNING! ACHTUNG! WARNING! ACHTUNG! WARNING! -- + + -- Developers: Please don't depend on the following functionality + -- to remain in the same place. Chances are very good these + -- functions will migrate to another PL/SQL package or be replaced + -- by direct calls to CR code in the near future. + + function new_file ( + message_id in acs_messages.message_id%TYPE, + file_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + function edit_file ( + file_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + procedure delete_file ( + file_id in cr_items.item_id%TYPE + ); + + function new_image ( + message_id in acs_messages.message_id%TYPE, + image_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + function edit_image ( + image_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + procedure delete_image ( + image_id in cr_items.item_id%TYPE + ); + + function new_extlink ( + name in cr_items.name%TYPE, + extlink_id in cr_extlinks.extlink_id%TYPE default null, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null, + parent_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null + ) return cr_extlinks.extlink_id%TYPE; + + function edit_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null + ) return cr_extlinks.extlink_id%TYPE; + + procedure delete_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE + ); + + function name ( + message_id in acs_objects.object_id%TYPE + ) return varchar2; + +end acs_message; +/ +show errors + +create or replace package body acs_message +as + + function new ( + message_id in acs_messages.message_id%TYPE default null, + reply_to in acs_messages.reply_to%TYPE default null, + sent_date in acs_messages.sent_date%TYPE default null, + sender in acs_messages.sender%TYPE default null, + rfc822_id in acs_messages.rfc822_id%TYPE default null, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + parent_id in cr_items.parent_id%TYPE default 0, + context_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_message', + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_message_id acs_messages.message_id%TYPE; + v_rfc822_id acs_messages.rfc822_id%TYPE; + v_revision_id cr_revisions.revision_id%TYPE; + begin + + -- generate a message id now so we can get an rfc822 message-id + if message_id is null then + select acs_object_id_seq.nextval into v_message_id from dual; + else + v_message_id := message_id; + end if; + + -- this needs to be fixed up, but Oracle doesn't give us a way + -- to get the FQDN + if rfc822_id is null then + v_rfc822_id := sysdate || '.' || v_message_id || '@' || + utl_inaddr.get_host_name || '.hate'; + else + v_rfc822_id := rfc822_id; + end if; + + v_message_id := content_item.new ( + name => v_rfc822_id, + parent_id => parent_id, + content_type => 'acs_message_revision', + item_id => message_id, + context_id => context_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + item_subtype => object_type + ); + + insert into acs_messages + (message_id, reply_to, sent_date, sender, rfc822_id) + values + (v_message_id, reply_to, sent_date, sender, v_rfc822_id); + + -- create an initial revision for the new message + v_revision_id := acs_message.edit ( + message_id => v_message_id, + title => title, + description => description, + mime_type => mime_type, + text => text, + data => data, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + is_live => is_live + ); + + return v_message_id; + end new; + + function edit ( + message_id in acs_messages.message_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_revision_id cr_revisions.revision_id%TYPE; + begin + + -- create a new revision using whichever call is appropriate + if edit.data is not null then + v_revision_id := content_revision.new ( + item_id => message_id, + title => title, + description => description, + data => data, + mime_type => mime_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + elsif title is not null or text is not null then + v_revision_id := content_revision.new ( + item_id => message_id, + title => title, + description => description, + text => text, + mime_type => mime_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + end if; + + -- test for auto approval of revision + if edit.is_live = 't' then + content_item.set_live_revision(v_revision_id); + end if; + + return v_revision_id; + + end edit; + + procedure delete ( + message_id in acs_messages.message_id%TYPE + ) + is + begin + delete from acs_messages + where message_id = acs_message.delete.message_id; + content_item.delete(message_id); + end delete; + + function message_p ( + message_id in acs_messages.message_id%TYPE + ) return char + is + v_check_message_id integer; + begin + select decode(count(message_id),0,0,1) into v_check_message_id + from acs_messages + where message_id = message_p.message_id; + if v_check_message_id <> 0 then + return 't'; + else + return 'f'; + end if; + end message_p; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + to_address in varchar2, + grouping_id in integer default null, + wait_until in date default sysdate + ) + is + v_wait_until date; + begin + v_wait_until := nvl(wait_until, sysdate); + insert into acs_messages_outgoing + (message_id, to_address, grouping_id, wait_until) + values + (message_id, to_address, grouping_id, v_wait_until); + end send; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + recipient_id in parties.party_id%TYPE, + grouping_id in integer default null, + wait_until in date default sysdate + ) + is + v_wait_until date; + begin + v_wait_until := nvl(wait_until, sysdate); + insert into acs_messages_outgoing + (message_id, to_address, grouping_id, wait_until) + select send.message_id, p.email, send.grouping_id, v_wait_until + from parties p + where p.party_id = send.recipient_id; + end send; + + function first_ancestor ( + message_id in acs_messages.message_id%TYPE + ) return acs_messages.message_id%TYPE + is + v_message_id acs_messages.message_id%TYPE; + begin + select message_id into v_message_id + from (select message_id, reply_to + from acs_messages + connect by message_id = prior reply_to + start with message_id = first_ancestor.message_id) ancestors + where reply_to is null; + return v_message_id; + end first_ancestor; + + -- ACHTUNG! WARNING! ACHTUNG! WARNING! ACHTUNG! WARNING! -- + + -- Developers: Please don't depend on the following functionality + -- to remain in the same place. Chances are very good these + -- functions will migrate to another PL/SQL package or be replaced + -- by direct calls to CR code in the near future. + + function new_file ( + message_id in acs_messages.message_id%TYPE, + file_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_file_id cr_items.item_id%TYPE; + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_file_id := content_item.new ( + name => file_name, + parent_id => message_id, + item_id => file_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + + -- create an initial revision for the new attachment + v_revision_id := edit_file ( + file_id => v_file_id, + title => title, + description => description, + mime_type => mime_type, + content => content, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + is_live => is_live + ); + + return v_file_id; + end new_file; + + function edit_file ( + file_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_revision_id := content_revision.new ( + title => title, + mime_type => mime_type, + data => content, + item_id => file_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + + -- test for auto approval of revision + if is_live = 't' then + content_item.set_live_revision(v_revision_id); + end if; + + return v_revision_id; + end edit_file; + + procedure delete_file ( + file_id in cr_items.item_id%TYPE + ) + is + begin + content_item.delete(delete_file.file_id); + end delete_file; + + function new_image ( + message_id in acs_messages.message_id%TYPE, + image_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_image_id cr_items.item_id%TYPE; + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_image_id := content_item.new ( + name => file_name, + parent_id => message_id, + item_id => image_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + + -- create an initial revision for the new attachment + v_revision_id := edit_image ( + image_id => v_image_id, + title => title, + description => description, + mime_type => mime_type, + content => content, + width => width, + height => height, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + is_live => is_live + ); + + return v_image_id; + end new_image; + + function edit_image ( + image_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_revision_id := content_revision.new ( + title => edit_image.title, + mime_type => edit_image.mime_type, + data => edit_image.content, + item_id => edit_image.image_id, + creation_date => edit_image.creation_date, + creation_user => edit_image.creation_user, + creation_ip => edit_image.creation_ip + ); + + -- insert new width and height values + -- XXX fix after image.new exists + insert into images + (image_id, width, height) + values + (v_revision_id, width, height); + + -- test for auto approval of revision + if edit_image.is_live = 't' then + content_item.set_live_revision(v_revision_id); + end if; + + return v_revision_id; + end edit_image; + + procedure delete_image ( + image_id in cr_items.item_id%TYPE + ) + is + begin + -- XXX fix after image.delete exists + delete from images + where image_id = delete_image.image_id; + content_item.delete(image_id); + end delete_image; + + -- XXX should just call content_extlink.new + function new_extlink ( + name in cr_items.name%TYPE default null, + extlink_id in cr_extlinks.extlink_id%TYPE default null, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null, + parent_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null + ) return cr_extlinks.extlink_id%TYPE + is + v_extlink_id cr_extlinks.extlink_id%TYPE; + begin + v_extlink_id := content_extlink.new ( + name => new_extlink.name, + url => new_extlink.url, + label => new_extlink.label, + description => new_extlink.description, + parent_id => new_extlink.parent_id, + extlink_id => new_extlink.extlink_id, + creation_date => new_extlink.creation_date, + creation_user => new_extlink.creation_user, + creation_ip => new_extlink.creation_ip + ); + end new_extlink; + + -- XXX should just edit extlink + function edit_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null + ) return cr_extlinks.extlink_id%TYPE + is + v_is_extlink char; + begin + v_is_extlink := content_extlink.is_extlink(edit_extlink.extlink_id); + if v_is_extlink = 't' then + update cr_extlinks + set url = edit_extlink.url, + label = edit_extlink.label, + description = edit_extlink.description + where extlink_id = edit_extlink.extlink_id; + end if; + return v_is_extlink; + end edit_extlink; + + procedure delete_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE + ) is + begin + content_extlink.delete(extlink_id => delete_extlink.extlink_id); + end delete_extlink; + + function name ( + message_id in acs_objects.object_id%TYPE + ) return varchar2 + is + v_message_name acs_messages_all.title%TYPE; + begin + select title into v_message_name + from acs_messages_all + where message_id = name.message_id; + return v_message_name; + end name; + +end acs_message; +/ +show errors Index: openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-views.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-views.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/acs-messaging-views.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,21 @@ +-- +-- packages/acs-messaging/sql/acs-messaging-create.sql +-- +-- @author John Prevost +-- @creation-date 2000-11-15 +-- @cvs-id $Id: acs-messaging-views.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +create or replace view acs_messages_all as + select m.message_id, m.reply_to, m.sent_date, m.sender, m.rfc822_id, + r.revision_id, r.title, r.mime_type, r.content + from cr_items i, cr_revisions r, acs_messages m + where i.item_id = m.message_id and r.revision_id = i.live_revision; + +create or replace view acs_messages_latest as + select m.message_id, m.reply_to, m.sent_date, m.sender, m.rfc822_id, + r.revision_id, r.title, r.mime_type, r.content + from cr_items i, cr_revisions r, acs_messages m + where i.item_id = m.message_id + and r.revision_id = content_item.get_latest_revision(i.item_id); + Index: openacs-4/packages/acs-messaging/sql/oracle/upgrade-4.0-4.0.1a.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/Attic/upgrade-4.0-4.0.1a.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/upgrade-4.0-4.0.1a.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,242 @@ +-- +-- acs-messaging sql/upgrade-4.0-4.0.1a.sql +-- +-- @author jmp@arsdigita.com +-- @creation-date 2000-11-03 +-- @cvs-id $Id: upgrade-4.0-4.0.1a.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +alter table acs_messages add ( + sent_date date + constraint acs_messages_sent_date_nn + not null + disable, + sender integer + constraint acs_messages_sender_fk + references parties (party_id) + disable, + rfc822_id varchar2(250) + constraint acs_messages_rfc822_id_nn + not null + disable + constraint acs_messages_rfc822_id_un + unique + disable +); + +create table acs_mess_up ( + id integer primary key, + sent_date date, + sender integer, + rfc822_id varchar2(250) +); + +insert into acs_mess_up + select m.message_id, + r.publish_date as sent_date, + o.creation_user as sender, + (sysdate || '.' || message_id || '@' + || utl_inaddr.get_host_name||'.hate') as rfc822_id + from acs_objects o, cr_items i, cr_revisions r, acs_messages m + where m.message_id = i.item_id + and m.message_id = o.object_id + and r.revision_id = i.live_revision; + +update acs_messages + set sent_date = (select sent_date from acs_mess_up where id = message_id), + sender = (select sender from acs_mess_up where id = message_id), + rfc822_id = (select rfc822_id from acs_mess_up where id = message_id); + +drop table acs_mess_up; + +alter table acs_messages modify constraint acs_messages_sent_date_nn enable; +alter table acs_messages modify constraint acs_messages_sender_fk enable; +alter table acs_messages modify constraint acs_messages_rfc822_id_nn enable; +alter table acs_messages modify constraint acs_messages_rfc822_id_un enable; + +create or replace view acs_messages_all as + select m.message_id, m.reply_to, m.sent_date, m.sender, m.rfc822_id, + r.title, r.mime_type, r.content, o.context_id + from acs_objects o, cr_items i, cr_revisions r, acs_messages m + where o.object_id = m.message_id and i.item_id = m.message_id + and r.revision_id = i.live_revision; + +create table acs_messages_outgoing ( + message_id integer + constraint amo_message_id_fk + references acs_messages (message_id) on delete cascade, + recipient_id integer + constraint amo_recipient_id_fk + references parties (party_id), + grouping_id integer, + wait_until date not null, + constraint acs_messages_outgoing_pk + primary key (message_id, recipient_id) +); + +comment on column acs_messages_outgoing.grouping_id is ' + This identifier is used to group sets of messages to be sent as + digests. When a message is about to be sent, any other messages + with the same grouping_id will be put together with it in a + digest. It is recommended but not required that an object id is + used. Bboard, for example, might use the forum id that the user''s + subscribed to. For instant (non-digest) updates, it would be + appropriate to use null, which is never equal to anything else. +'; + +comment on column acs_messages_outgoing.wait_until is ' + Don''t schedule a send until after this date. If another message with + the same grouping ID is scheduled to be sent, then this message may be + sent at the same time. (So, for example, daily digests would be + achieved by setting the grouping_id to the same value, and the wait_until + value to the end of the current day. As soon as one message in the group + is to be sent, all will be sent.) +'; + +create or replace package acs_message +as + + function new ( + message_id in acs_messages.message_id%TYPE default null, + reply_to in acs_messages.reply_to%TYPE default null, + sent_date in acs_messages.sent_date%TYPE default sysdate, + sender in acs_messages.sender%TYPE default null, + rfc822_id in acs_messages.rfc822_id%TYPE default null, + title in cr_revisions.title%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + context_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_message' + ) return acs_objects.object_id%TYPE; + + procedure delete ( + message_id in acs_messages.message_id%TYPE + ); + + function message_p ( + message_id in acs_messages.message_id%TYPE + ) return char; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + recipient_id in parties.party_id%TYPE, + grouping_id in integer default NULL, + wait_until in date default SYSDATE + ); + +end acs_message; +/ +show errors + +create or replace package body acs_message +as + + function new ( + message_id in acs_messages.message_id%TYPE default null, + reply_to in acs_messages.reply_to%TYPE default null, + sent_date in acs_messages.sent_date%TYPE default sysdate, + sender in acs_messages.sender%TYPE default null, + rfc822_id in acs_messages.rfc822_id%TYPE default null, + title in cr_revisions.title%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + context_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_message' + ) return acs_objects.object_id%TYPE + is + v_message_id acs_messages.message_id%TYPE; + v_rfc822_id acs_messages.rfc822_id%TYPE; + v_name cr_items.name%TYPE; + begin + if message_id is null then + select acs_object_id_seq.nextval into v_message_id from dual; + else + v_message_id := message_id; + end if; + + if rfc822_id is null then + v_rfc822_id := sysdate || '.' || v_message_id || '@' || + utl_inaddr.get_host_name || '.hate'; + else + v_rfc822_id := rfc822_id; + end if; + + v_name := v_rfc822_id; + + v_message_id := content_item.new ( + name => v_name, + parent_id => context_id, + item_id => message_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + item_subtype => object_type, + title => title, + mime_type => mime_type, + text => text, + data => data, + is_live => 't' + ); + + -- I hate you, milkman CR. + -- Fix the broken permissions stuff content_item.new does + update acs_objects set security_inherit_p = 't' + where object_id = v_message_id; + delete from acs_permissions where object_id = v_message_id; + + insert into + acs_messages (message_id, reply_to, sent_date, sender, rfc822_id) + values (v_message_id, reply_to, sent_date, sender, v_rfc822_id); + + return v_message_id; + end new; + + procedure delete ( + message_id in acs_messages.message_id%TYPE + ) + is + begin + delete from acs_messages + where message_id = acs_message.delete.message_id; + content_item.delete(message_id); + end; + + function message_p ( + message_id in acs_messages.message_id%TYPE + ) return char + is + v_check_message_id char(1); + begin + select decode(count(message_id),0,'f','t') into v_check_message_id + from acs_messages + where message_id = message_p.message_id; + return v_check_message_id; + end message_p; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + recipient_id in parties.party_id%TYPE, + grouping_id in integer default NULL, + wait_until in date default SYSDATE + ) + is + v_wait_until date; + begin + v_wait_until := nvl(wait_until, SYSDATE); + insert into acs_messages_outgoing + (message_id, recipient_id, grouping_id, wait_until) + values + (message_id, recipient_id, grouping_id, nvl(wait_until,SYSDATE)); + end; + +end acs_message; +/ +show errors Index: openacs-4/packages/acs-messaging/sql/oracle/upgrade-4.0.1-4.1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/Attic/upgrade-4.0.1-4.1.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/upgrade-4.0.1-4.1.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,12 @@ +-- +-- acs-messaging sql/upgrade-4.0.1-4.1.sql +-- +-- @author John Prevost +-- @creation-date 2001-01-16 +-- @cvs-id $Id: upgrade-4.0.1-4.1.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +-- do all the views and packages in case something changed + +@@ acs-messaging-views +@@ acs-messaging-packages \ No newline at end of file Index: openacs-4/packages/acs-messaging/sql/oracle/upgrade-4.0.1a-4.0.1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/Attic/upgrade-4.0.1a-4.0.1.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/upgrade-4.0.1a-4.0.1.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,49 @@ +-- +-- acs-messaging sql/upgrade-4.0.1a-4.0.1.sql +-- +-- @author jmp@arsdigita.com +-- @creation-date 2000-11-15 +-- @cvs-id $Id: upgrade-4.0.1a-4.0.1.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +begin + acs_object_type.create_type ( + supertype => 'content_revision', + object_type => 'acs_message_revision', + pretty_name => 'Message Revision', + pretty_plural => 'Message Revisions', + table_name => 'CR_REVISIONS', + id_column => 'REVISION_ID', + name_method => 'ACS_OBJECT.DEFAULT_NAME' + ); +end; +/ +show errors + +alter table acs_messages_outgoing add ( + to_address varchar2(1000) + constraint amo_to_address_nn + not null + disable +); + +update acs_messages_outgoing + set to_address = (select email from parties where party_id = recipient_id); + +alter table acs_messages_outgoing + drop constraint acs_messages_outgoing_pk; + +alter table acs_messages_outgoing + add constraint acs_messages_outgoing_pk + primary key (message_id, to_address); + +alter table acs_messages_outgoing + modify constraint amo_to_address_nn enable; + +alter table acs_messages_outgoing + drop column recipient_id; + +@@ acs-messaging-views +@@ acs-messaging-packages + +set feedback on Index: openacs-4/packages/acs-messaging/sql/oracle/upgrade/upgrade-4.1-4.1.1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-messaging/sql/oracle/upgrade/upgrade-4.1-4.1.1.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-messaging/sql/oracle/upgrade/upgrade-4.1-4.1.1.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,658 @@ +-- packages/acs-messaging/sql/upgrade/sql/upgrade/upgrade-4.1-4.1.1.sql +-- +-- upgrade script for acs-messaging 4.1 to 4.1.1. +-- @author teeters@arsdigita.com +-- @creation-date 2000-03-06 +-- @cvs-id $Id: upgrade-4.1-4.1.1.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +-- Change in acs-messaging/sql/acs-messaging-create.sql +-- content_item name_method changed from 'ACS_OBJECT.DEFAULT_NAME' to 'ACS_MESSAGE.NAME' + +update acs_object_types set name_method = 'ACS_MESSAGE.NAME' where object_type = 'acs_message'; + +-- Added function name to package acs_message; + +-- @../../../acs-messaging/sql/acs-messaging-packages.sql + +-- would like to source file using @, but for some reason source not working +-- have to copy file. + +-- +-- packages/acs-messaging/sql/acs-messaging-packages.sql +-- +-- @author John Prevost +-- @author Phong Nguyen +-- @creation-date 2000-08-27 +-- @cvs-id $Id: upgrade-4.1-4.1.1.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +create or replace package acs_message +as + + function new ( + message_id in acs_messages.message_id%TYPE default null, + reply_to in acs_messages.reply_to%TYPE default null, + sent_date in acs_messages.sent_date%TYPE default sysdate, + sender in acs_messages.sender%TYPE default null, + rfc822_id in acs_messages.rfc822_id%TYPE default null, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + parent_id in cr_items.parent_id%TYPE default 0, + context_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_message', + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + function edit ( + message_id in acs_messages.message_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + procedure delete ( + message_id in acs_messages.message_id%TYPE + ); + + function message_p ( + message_id in acs_messages.message_id%TYPE + ) return char; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + recipient_id in parties.party_id%TYPE, + grouping_id in integer default null, + wait_until in date default sysdate + ); + + procedure send ( + message_id in acs_messages.message_id%TYPE, + to_address in varchar2, + grouping_id in integer default null, + wait_until in date default sysdate + ); + + function first_ancestor ( + message_id in acs_messages.message_id%TYPE + ) return acs_messages.message_id%TYPE; + + -- ACHTUNG! WARNING! ACHTUNG! WARNING! ACHTUNG! WARNING! -- + + -- Developers: Please don't depend on the following functionality + -- to remain in the same place. Chances are very good these + -- functions will migrate to another PL/SQL package or be replaced + -- by direct calls to CR code in the near future. + + function new_file ( + message_id in acs_messages.message_id%TYPE, + file_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + function edit_file ( + file_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + procedure delete_file ( + file_id in cr_items.item_id%TYPE + ); + + function new_image ( + message_id in acs_messages.message_id%TYPE, + image_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + function edit_image ( + image_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE, + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE; + + procedure delete_image ( + image_id in cr_items.item_id%TYPE + ); + + function new_extlink ( + name in cr_items.name%TYPE, + extlink_id in cr_extlinks.extlink_id%TYPE default null, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null, + parent_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null + ) return cr_extlinks.extlink_id%TYPE; + + function edit_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null + ) return cr_extlinks.extlink_id%TYPE; + + procedure delete_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE + ); + + function name ( + message_id in acs_objects.object_id%TYPE + ) return varchar2; + +end acs_message; +/ +show errors + +create or replace package body acs_message +as + + function new ( + message_id in acs_messages.message_id%TYPE default null, + reply_to in acs_messages.reply_to%TYPE default null, + sent_date in acs_messages.sent_date%TYPE default null, + sender in acs_messages.sender%TYPE default null, + rfc822_id in acs_messages.rfc822_id%TYPE default null, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + parent_id in cr_items.parent_id%TYPE default 0, + context_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + object_type in acs_objects.object_type%TYPE default 'acs_message', + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_message_id acs_messages.message_id%TYPE; + v_rfc822_id acs_messages.rfc822_id%TYPE; + v_revision_id cr_revisions.revision_id%TYPE; + begin + + -- generate a message id now so we can get an rfc822 message-id + if message_id is null then + select acs_object_id_seq.nextval into v_message_id from dual; + else + v_message_id := message_id; + end if; + + -- this needs to be fixed up, but Oracle doesn't give us a way + -- to get the FQDN + if rfc822_id is null then + v_rfc822_id := sysdate || '.' || v_message_id || '@' || + utl_inaddr.get_host_name || '.hate'; + else + v_rfc822_id := rfc822_id; + end if; + + v_message_id := content_item.new ( + name => v_rfc822_id, + parent_id => parent_id, + content_type => 'acs_message_revision', + item_id => message_id, + context_id => context_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + item_subtype => object_type + ); + + insert into acs_messages + (message_id, reply_to, sent_date, sender, rfc822_id) + values + (v_message_id, reply_to, sent_date, sender, v_rfc822_id); + + -- create an initial revision for the new message + v_revision_id := acs_message.edit ( + message_id => v_message_id, + title => title, + description => description, + mime_type => mime_type, + text => text, + data => data, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + is_live => is_live + ); + + return v_message_id; + end new; + + function edit ( + message_id in acs_messages.message_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + text in varchar2 default null, + data in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_revision_id cr_revisions.revision_id%TYPE; + begin + + -- create a new revision using whichever call is appropriate + if edit.data is not null then + v_revision_id := content_revision.new ( + item_id => message_id, + title => title, + description => description, + data => data, + mime_type => mime_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + elsif title is not null or text is not null then + v_revision_id := content_revision.new ( + item_id => message_id, + title => title, + description => description, + text => text, + mime_type => mime_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + end if; + + -- test for auto approval of revision + if edit.is_live = 't' then + content_item.set_live_revision(v_revision_id); + end if; + + return v_revision_id; + + end edit; + + procedure delete ( + message_id in acs_messages.message_id%TYPE + ) + is + begin + delete from acs_messages + where message_id = acs_message.delete.message_id; + content_item.delete(message_id); + end delete; + + function message_p ( + message_id in acs_messages.message_id%TYPE + ) return char + is + v_check_message_id integer; + begin + select decode(count(message_id),0,0,1) into v_check_message_id + from acs_messages + where message_id = message_p.message_id; + if v_check_message_id <> 0 then + return 't'; + else + return 'f'; + end if; + end message_p; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + to_address in varchar2, + grouping_id in integer default null, + wait_until in date default sysdate + ) + is + v_wait_until date; + begin + v_wait_until := nvl(wait_until, sysdate); + insert into acs_messages_outgoing + (message_id, to_address, grouping_id, wait_until) + values + (message_id, to_address, grouping_id, v_wait_until); + end send; + + procedure send ( + message_id in acs_messages.message_id%TYPE, + recipient_id in parties.party_id%TYPE, + grouping_id in integer default null, + wait_until in date default sysdate + ) + is + v_wait_until date; + begin + v_wait_until := nvl(wait_until, sysdate); + insert into acs_messages_outgoing + (message_id, to_address, grouping_id, wait_until) + select send.message_id, p.email, send.grouping_id, v_wait_until + from parties p + where p.party_id = send.recipient_id; + end send; + + function first_ancestor ( + message_id in acs_messages.message_id%TYPE + ) return acs_messages.message_id%TYPE + is + v_message_id acs_messages.message_id%TYPE; + begin + select message_id into v_message_id + from (select message_id, reply_to + from acs_messages + connect by message_id = prior reply_to + start with message_id = first_ancestor.message_id) ancestors + where reply_to is null; + return v_message_id; + end first_ancestor; + + -- ACHTUNG! WARNING! ACHTUNG! WARNING! ACHTUNG! WARNING! -- + + -- Developers: Please don't depend on the following functionality + -- to remain in the same place. Chances are very good these + -- functions will migrate to another PL/SQL package or be replaced + -- by direct calls to CR code in the near future. + + function new_file ( + message_id in acs_messages.message_id%TYPE, + file_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_file_id cr_items.item_id%TYPE; + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_file_id := content_item.new ( + name => file_name, + parent_id => message_id, + item_id => file_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + + -- create an initial revision for the new attachment + v_revision_id := edit_file ( + file_id => v_file_id, + title => title, + description => description, + mime_type => mime_type, + content => content, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + is_live => is_live + ); + + return v_file_id; + end new_file; + + function edit_file ( + file_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_revision_id := content_revision.new ( + title => title, + mime_type => mime_type, + data => content, + item_id => file_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + + -- test for auto approval of revision + if is_live = 't' then + content_item.set_live_revision(v_revision_id); + end if; + + return v_revision_id; + end edit_file; + + procedure delete_file ( + file_id in cr_items.item_id%TYPE + ) + is + begin + content_item.delete(delete_file.file_id); + end delete_file; + + function new_image ( + message_id in acs_messages.message_id%TYPE, + image_id in cr_items.item_id%TYPE default null, + file_name in cr_items.name%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_image_id cr_items.item_id%TYPE; + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_image_id := content_item.new ( + name => file_name, + parent_id => message_id, + item_id => image_id, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip + ); + + -- create an initial revision for the new attachment + v_revision_id := edit_image ( + image_id => v_image_id, + title => title, + description => description, + mime_type => mime_type, + content => content, + width => width, + height => height, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + is_live => is_live + ); + + return v_image_id; + end new_image; + + function edit_image ( + image_id in cr_items.item_id%TYPE, + title in cr_revisions.title%TYPE default null, + description in cr_revisions.description%TYPE default null, + mime_type in cr_revisions.mime_type%TYPE default 'text/plain', + content in cr_revisions.content%TYPE default null, + width in images.width%TYPE default null, + height in images.height%TYPE default null, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + is_live in char default 't' + ) return acs_objects.object_id%TYPE + is + v_revision_id cr_revisions.revision_id%TYPE; + begin + + v_revision_id := content_revision.new ( + title => edit_image.title, + mime_type => edit_image.mime_type, + data => edit_image.content, + item_id => edit_image.image_id, + creation_date => edit_image.creation_date, + creation_user => edit_image.creation_user, + creation_ip => edit_image.creation_ip + ); + + -- insert new width and height values + -- XXX fix after image.new exists + insert into images + (image_id, width, height) + values + (v_revision_id, width, height); + + -- test for auto approval of revision + if edit_image.is_live = 't' then + content_item.set_live_revision(v_revision_id); + end if; + + return v_revision_id; + end edit_image; + + procedure delete_image ( + image_id in cr_items.item_id%TYPE + ) + is + begin + -- XXX fix after image.delete exists + delete from images + where image_id = delete_image.image_id; + content_item.delete(image_id); + end delete_image; + + -- XXX should just call content_extlink.new + function new_extlink ( + name in cr_items.name%TYPE default null, + extlink_id in cr_extlinks.extlink_id%TYPE default null, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null, + parent_id in acs_objects.context_id%TYPE, + creation_date in acs_objects.creation_date%TYPE default sysdate, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null + ) return cr_extlinks.extlink_id%TYPE + is + v_extlink_id cr_extlinks.extlink_id%TYPE; + begin + v_extlink_id := content_extlink.new ( + name => new_extlink.name, + url => new_extlink.url, + label => new_extlink.label, + description => new_extlink.description, + parent_id => new_extlink.parent_id, + extlink_id => new_extlink.extlink_id, + creation_date => new_extlink.creation_date, + creation_user => new_extlink.creation_user, + creation_ip => new_extlink.creation_ip + ); + end new_extlink; + + -- XXX should just edit extlink + function edit_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE, + url in cr_extlinks.url%TYPE, + label in cr_extlinks.label%TYPE default null, + description in cr_extlinks.description%TYPE default null + ) return cr_extlinks.extlink_id%TYPE + is + v_is_extlink char; + begin + v_is_extlink := content_extlink.is_extlink(edit_extlink.extlink_id); + if v_is_extlink = 't' then + update cr_extlinks + set url = edit_extlink.url, + label = edit_extlink.label, + description = edit_extlink.description + where extlink_id = edit_extlink.extlink_id; + end if; + return v_is_extlink; + end edit_extlink; + + procedure delete_extlink ( + extlink_id in cr_extlinks.extlink_id%TYPE + ) is + begin + content_extlink.delete(extlink_id => delete_extlink.extlink_id); + end delete_extlink; + + function name ( + message_id in acs_objects.object_id%TYPE + ) return varchar2 + is + v_message_name acs_messages_all.title%TYPE; + begin + select title into v_message_name + from acs_messages_all + where message_id = name.message_id; + return v_message_name; + end name; + +end acs_message; +/ +show errors + + Index: openacs-4/packages/acs-notification/acs-notification.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-notification/Attic/acs-notification.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-notification/acs-notification.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-notification/acs-notification.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Stanislav Freidin The ACS Notification Service provides an API for the RDBMS to send email notifications. ArsDigita Corporation @@ -20,9 +21,9 @@ - - - + + + Index: openacs-4/packages/acs-notification/sql/oracle/acs-notification-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-notification/sql/oracle/Attic/acs-notification-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-notification/sql/oracle/acs-notification-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,111 @@ +---------------------------------------------------------- +-- Create the notification data model, which supports the +-- message queue. +---------------------------------------------------------- + + +-- The notification queue which holds all the notification requests, +-- pending or processed + +create table nt_requests ( + -- Unique request id + request_id integer constraint nt_request_pk + primary key, + -- The party to whom this message is being sent + party_to integer not null constraint nt_request_party_to_ref + references parties, + -- If the target party is a group, do we completely flatten + -- it, thus sending email to individual users, or do we send the + -- email to the group if it has an email address ? + expand_group char(1) default 'f' not null + constraint nt_request_expand_chk check(expand_group in ('t', 'f')), + -- The party who is sending this message + -- Doesn't really have to have an email field... ? + party_from integer not null constraint nt_request_party_from_ref + references parties, + -- The message that will be sent + message clob, + -- One line of subject text for the message + subject varchar2(1000), + -- The date on which the posting to the queue was made + request_date date default sysdate, + -- The date on which the request was fulfilled + fulfill_date date, + -- The status of the request + -- pending: no attempt made to send it yet + -- sent: sent successfully + -- partial: an attempt to send the request has been made, but not all of + -- the users in the target group have been reached + -- partial_sent: some of the messages went through, others we gave up on + -- failed: check the error_code and error_message columns + -- cancelled: request was aborted + status varchar2(20) default 'pending' + constraint nt_request_status_chk + check(status in ('pending', 'sent', 'sending', 'partial_failure', 'failed', 'cancelled')), + -- How many times will we try to send this message ? + max_retries integer default 3 not null +); + +create sequence nt_request_seq start with 1000; + +create index nt_request_expand_idx on nt_requests + (expand_group, request_date, party_to); + +create index nt_requests_party_to_idx on nt_requests (party_to); +create index nt_requests_party_from_idx on nt_requests (party_from); + +-- The table that holds all the neccessary SMTP information for individual +-- users + +create table nt_queue ( + -- Request id + request_id integer constraint nt_queue_request_ref + references nt_requests on delete cascade, + -- The individual user to whom email is being sent + -- Not neccessarily the same as nt_requests.party_id + party_to integer constraint nt_queue_party_to_ref + references parties on delete cascade, + -- Composite primary key + primary key(request_id, party_to), + -- SMTP reply code (250 means ok) + smtp_reply_code integer, + -- SMTP text reply message + smtp_reply_message varchar2(4000), + -- How many times have we already tried to send this message ? + retry_count integer default 0 not null, + -- Did we succeed in sending this message ? + -- This is really just syntactic sugar, since we can just look at the + -- smtp_reply_code + is_successful char(1) default 'f' not null + constraint nt_queue_is_successful_chk + check (is_successful in ('t', 'f')) +); + +create index nt_queue_success_idx on nt_queue + (request_id, is_successful, retry_count); + +-- This table keeps track of the job id for scheduling the queue +-- processing, and some miscellaneous statisticc + +create table nt_job ( + job_id integer, + last_run_date date +); + +insert into nt_job (job_id, last_run_date) values (null, null); + +-- Make sure that only one value can exist in the nt_job table +create or replace trigger nt_job_tr +before insert or delete on nt_job +begin + raise_application_error(-20000, + 'Table nt_job must have exactly one row.' + ); +end; +/ +show errors + +prompt *** Compiling mail utility package... +@@ mail-package.sql +prompt *** Compiling notification package... +@@ acs-notification-package.sql Index: openacs-4/packages/acs-notification/sql/oracle/acs-notification-package.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-notification/sql/oracle/Attic/acs-notification-package.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-notification/sql/oracle/acs-notification-package.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,520 @@ +------------------------------------------------ +-- The procedures used by the notification module +------------------------------------------------ + +create or replace package nt +is + +-- Post a new request, return its id +function post_request ( + --/** Post a notification request, which will be processed at a later + -- time + -- @author Stanislav Freidin + -- @param party_from The id of the sending party + -- @param party_to The id of the sending party + -- @param expand_group If 't', the party_to is treated as a group + -- and an individual notification is sent to each member + -- of the group. If 'f', only one notification is sent + -- to the party_to's email address. + -- @param subject A one-line subject for the message + -- @param message The body of the message, 4000 characters maximum + -- @max_retries The number of time to try before giving up on the + -- notification, defaults to 3 + -- @return The id of the new request + -- @see {notification.cancel_request} + --*/ + party_from IN nt_requests.party_from%TYPE, + party_to IN nt_requests.party_to%TYPE, + expand_group IN nt_requests.expand_group%TYPE, + subject IN nt_requests.subject%TYPE, + message IN varchar2, + max_retries IN nt_requests.max_retries%TYPE default 3 +) return nt_requests.request_id%TYPE; + +-- Cancel a request, marking all messages in the queue as failed +procedure cancel_request ( + --/** Cancel a notification requests. Mark all notifications that were generated + -- by the request as Failed. + -- @author Stanislav Freidin + -- @param request_id Id of the request to cancel + -- @see {notification.post_request} + --*/ + request_id IN nt_requests.request_id%TYPE +); + +-- Flatten the pending requests into the notification queue, expanding +-- each target group to its individual members +procedure expand_requests + --/** This is a helper procedure and is not part of the public API. + -- It expands all pending requests, creating at least one row in + -- nt_queue for each request. + -- @author Stanislav Freidin + -- @see {notification.post_request} + --*/ +; + +-- Update the requests table to reflect the new status +-- * If all messages have failed, change status to "failed" +-- * If all messages have succeeded, change status to "sent" +-- * If some messages can still be retried, change status to "sending" +-- This is hideously inefficient - would it be better to include the +-- total_messages and sent_messages columns in the nt_requests table ? +procedure update_requests + --/** This is a helper procedure and is not part of the public API. + -- It updates all requests in the nt_requests table, setting + -- the status as following:
    + --
  • If all messages have failed, change status to 'failed'
  • + --
  • If all messages have succeeded, change status to 'sent'
  • + --
  • If some messages have failed, but it is still possible to retry + -- some messages, change status to 'sending'
  • + --
  • If some messages have failed, and it is not possible to retry + -- any of them, set status to 'partial_failure'
+ -- @author Stanislav Freidin + -- @see {notification.expand_requests}, {notification.process_queue} + --*/ +; + +-- This procedure will be run periodically by DBMS_JOB. +-- It will collect the pending requests, expand them if neccessary, and +-- then email them to the parties. +procedure process_queue ( + --/** This procedure will be run periodically, by dbms_job. + -- The procedure will process the request queue, expand any pending + -- requests, combine notifications with the same from/to parties, + -- and send them out + -- @author Stanislav Freidin + -- @param host The hostname of the mailserver, such as 'mail.arsdigita.com' + -- @param port The port on which the mailserver expects a connection, + -- defaults to 25 + -- @see {notification.schedule_process} + --*/ + host IN varchar2, + port IN pls_integer default 25 +); + +-- Schedule the queue to be processed at a regular interval +-- The interval is the number of minutes between each job run +-- If the interval is null, cancels the job. +procedure schedule_process ( + --/** Schedule the processing of the queue at regular intervals. Shorter + -- intervals will mean more prompt processing of the requests, but may + -- place too much strain on the server. + -- @author Stanislav Freidin + -- @param interval The number of minutes between processing of the queue. If + -- the interval is null, cancels processing of the queue + -- until schedule_process is called again with + -- a non-null interval + -- @param host The hostname of the mailserver, such as 'mail.arsdigita.com' + -- @param port The port on which the mailserver expects a connection, + -- defaults to 25 + -- @see {notification.process_queue} + --*/ + interval IN number, + host IN varchar2, + port IN integer default 25 +); + + +end nt; +/ +show errors + + +create or replace package body nt +is + + +function post_request ( + party_from IN nt_requests.party_from%TYPE, + party_to IN nt_requests.party_to%TYPE, + expand_group IN nt_requests.expand_group%TYPE, + subject IN nt_requests.subject%TYPE, + message IN varchar2, + max_retries IN nt_requests.max_retries%TYPE +) return nt_requests.request_id%TYPE +is + v_clob_loc clob; + v_id nt_requests.request_id%TYPE; +begin + + select nt_request_seq.nextval into v_id from dual; + + insert into nt_requests + (request_id, party_from, party_to, expand_group, + subject, message, status, max_retries) + values + (v_id, party_from, party_to, expand_group, + subject, empty_clob(), 'pending', max_retries) + returning + message into v_clob_loc; + + dbms_lob.writeappend(v_clob_loc, length(message), message); + + return v_id; + +end post_request; + +procedure cancel_request ( + request_id IN nt_requests.request_id%TYPE +) +is + v_max_retries nt_requests.max_retries%TYPE; +begin + + select max_retries + 1 into v_max_retries + from nt_requests where request_id = request_id; + + -- Set all the pending messages in the queue to failure + update nt_queue set + is_successful = 'f', retry_count = v_max_retries + where + request_id = request_id; + + update nt_requests set + status = 'cancelled' + where + request_id = request_id; +end cancel_request; + + +procedure expand_requests +is + cursor c_expanded_cur is + select + r1.request_id, + NVL(m.member_id, r1.party_to) as party_to, + r1.request_date + from + nt_requests r1, group_approved_member_map m + where + r1.status = 'pending' + and + r1.expand_group = 't' + and + m.group_id(+) = r1.party_to + union select + r2.request_id, + r2.party_to, + r2.request_date + from + nt_requests r2 + where + r2.status = 'pending' + and + r2.expand_group = 'f' + order by + request_date; + + c_request_row c_expanded_cur%ROWTYPE; + +begin + + for c_request_row in c_expanded_cur loop + insert into nt_queue + (request_id, party_to) + values + (c_request_row.request_id, c_request_row.party_to); + end loop; + + -- Record the fact that these requests were expanded + update nt_requests set status='sending' where status='pending'; +end expand_requests; + + +procedure update_requests +is +begin + + -- If there were no failures, request is successful + update nt_requests set + status = 'sent', fulfill_date = sysdate + where + status = 'sending' + and not exists + (select 1 from nt_queue + where request_id = nt_requests.request_id + and is_successful = 'f'); + + -- If there were no successes, and we gave up, request has failed + update nt_requests set + status = 'failed' + where + status = 'sending' + and not exists + (select 1 from nt_queue + where request_id = nt_requests.request_id + and (is_successful = 't' or + (is_successful = 'f' and retry_count < nt_requests.max_retries))); + + -- If there were some successes, but we gave up, this is a partial failure + update nt_requests set + status = 'partial_failure', fulfill_date = sysdate + where + status = 'sending' + and exists + (select 1 from nt_queue + where request_id = nt_requests.request_id + and is_successful = 't') + and exists + (select 1 from nt_queue + where request_id = nt_requests.request_id + and (is_successful = 'f' and retry_count >= nt_requests.max_retries)) + and not exists + (select 1 from nt_queue + where request_id = nt_requests.request_id + and (is_successful = 'f' and retry_count < nt_requests.max_retries)); + +end update_requests; + +procedure process_queue ( + host IN varchar2, + port IN pls_integer default 25 +) +is + v_mail_conn utl_smtp.connection; + v_mail_reply utl_smtp.reply; + + -- Cursor that loops through individual messages, processing them + -- Only look at the messages which still have a chance of being sent out + cursor c_queue_cur is + select + q.party_to, q.retry_count, q.is_successful, + r.party_from, r.message, r.subject, r.request_date, + mail.to_email_date(r.request_date) as pretty_request_date, + r.max_retries, r.request_id, + acs_object.name(q.party_to) name_to, + pto.email as email_to, + acs_object.name(r.party_from) name_from, + pfrom.email as email_from + from + nt_queue q, nt_requests r, parties pto, parties pfrom + where + q.is_successful <> 't' + and + q.request_id = r.request_id + and + pfrom.party_id = r.party_from + and + pto.party_id = q.party_to + and + pto.email is not null + and + q.retry_count < r.max_retries + and + r.status = 'sending' + order by + r.party_from, q.party_to; + + c_queue_row c_queue_cur%ROWTYPE; + + v_old_party_from parties.party_id%TYPE := -1; + v_old_party_to parties.party_id%TYPE := -1; + v_ready_for_data char(1) := 'f'; + v_newline varchar2(10) := ' +'; + + message_failed exception; + v_num_requests integer; + +begin + + -- Record the time + update nt_job set last_run_date = sysdate; + + -- Determine if we have anything to do + select decode(count(*),0,0,1) into v_num_requests from nt_requests + where status in ('pending', 'sending'); + if v_num_requests < 1 then + return; + end if; + + -- Attempt to open connection, mark all items in the queue as failed + -- if this could not be done + begin + v_mail_reply := mail.open_connection(host, port, v_mail_conn); + if v_mail_reply.code <> 250 then + raise_application_error(-20000, 'Unable to open connection to ' || host || ':' || port); + end if; + exception + when others then + + -- Update all pending requests to failure + update + nt_queue + set + retry_count = retry_count + 1, + smtp_reply_code = v_mail_reply.code, + smtp_reply_message = v_mail_reply.text + where + is_successful = 'f' + and + retry_count < (select max_retries from nt_requests + where request_id = nt_queue.request_id); + + update_requests(); + + begin + -- Just in case + mail.close_connection(v_mail_conn); + exception + when others then null; + end; + + return; + end; + + -- Expand the pending requests + expand_requests(); + + -- Now process individual rows, collecting their individual messages + -- into a big chunk before sending the entire chunk + + for c_queue_row in c_queue_cur loop + + begin + + if v_ready_for_data = 't' and + (c_queue_row.party_from <> v_old_party_from or + c_queue_row.party_to <> v_old_party_to) then + -- Close old connection, if any + v_mail_reply := mail.close_data(v_mail_conn); + v_ready_for_data := 'f'; + end if; + + -- Prepare to send data, if neccessary + if v_ready_for_data <> 't' then + + -- Set up the sender + if c_queue_row.email_from is not null then + v_mail_reply := mail.mail_from(v_mail_conn, c_queue_row.email_from); + else + v_mail_reply := mail.mail_from(v_mail_conn, 'unknown@unknown.com'); + end if; + if v_mail_reply.code <> 250 then + raise message_failed; + end if; + -- Set up the recepient + v_mail_reply := mail.rcpt_to(v_mail_conn, c_queue_row.email_to); + if v_mail_reply.code not in (250, 251) then + raise message_failed; + end if; + -- Prepare to write data + v_mail_reply := mail.open_data(v_mail_conn); + + -- Write headers + mail.write_data_headers ( + v_mail_conn, + c_queue_row.email_from, c_queue_row.email_to, + c_queue_row.subject, c_queue_row.request_date + ); + + v_ready_for_data := 't'; + + end if; + + -- Once we have a working connection, write into it + mail.write_data_string( + v_mail_conn, + v_newline || v_newline ||'Message sent on ' || c_queue_row.pretty_request_date || + ' regarding ' || c_queue_row.subject || v_newline || v_newline); + + mail.write_data_clob(v_mail_conn, c_queue_row.message); + + -- Success: mark this entry and go on to the next one + update nt_queue set + is_successful = 't' + where + request_id = c_queue_row.request_id + and + party_to = c_queue_row.party_to; + + exception + when utl_smtp.transient_error or + utl_smtp.permanent_error or + message_failed + then + + -- Error sending mail: register that an error has occurred, go on to the next message + update nt_queue set + is_successful = 'f', retry_count = retry_count + 1, + smtp_reply_code = v_mail_reply.code, + smtp_reply_message = v_mail_reply.text + where + request_id = c_queue_row.request_id + and + party_to = c_queue_row.party_to; + + -- Just in case, close the data connection + if v_ready_for_data = 't' then + v_mail_reply := mail.close_data(v_mail_conn); + v_ready_for_data := 'f'; + end if; + + end; + + v_old_party_from := c_queue_row.party_from; + v_old_party_to := c_queue_row.party_to; + + end loop; + + -- Update the requests to reflect new status + update_requests(); + + if v_ready_for_data = 't' then + v_mail_reply := mail.close_data(v_mail_conn); + end if; + + mail.close_connection(v_mail_conn); + +end process_queue; + +procedure schedule_process ( + interval IN number, + host IN varchar2, + port IN integer default 25 +) +is + v_job_id integer := null; +begin + + -- Check if we have an existing job + begin + select job_id into v_job_id from nt_job; + exception + when no_data_found then null; + end; + + -- Are we cancelling a job ? + if interval is null then + if v_job_id is not null then + dbms_job.remove(v_job_id); + update nt_job set job_id = null; + end if; + else + -- We are inserting a new job or changing the interval + if v_job_id is not null then + dbms_job.remove(v_job_id); + end if; + + dbms_job.submit( + v_job_id, + 'nt.process_queue(''' || host || ''', ' || port || ');', + sysdate, + 'sysdate + ' || (interval/24/60), + false, + dbms_job.any_instance, + true + ); + + update nt_job set job_id = v_job_id, last_run_date = null; + + end if; + +end schedule_process; + +end nt; +/ +show errors; + + + Index: openacs-4/packages/acs-notification/sql/oracle/mail-package.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-notification/sql/oracle/Attic/mail-package.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-notification/sql/oracle/mail-package.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,438 @@ +-------------------------------------------------- +-- Wrapper procedures for utl_smtp, correcting +-- some of the bugs and providing utility functions +-------------------------------------------------- + +create or replace package mail +is + +-- Open the connection and send "helo me" +function open_connection ( + --/** Open a mail connection, and prepare to identify the user + -- @author Stanislav Freidin + -- @param host The hostname of the mailsrver, such as + -- 'mail.arsdigita.com' + -- @param port The port on which the mailserver is expecting a + -- connection, usually 25 + -- @param mail_conn The mail connection record. Information about + -- the opened connection will be returned here + -- @return The reply code record from the mailserver. The expected reply + -- code is 250. + -- @see {mail.mail_from}, {mail.send_mail}, Oracle's utl_smtp package + --*/ + host IN varchar2, + port IN pls_integer, + mail_conn OUT NOCOPY utl_smtp.connection +) return utl_smtp.reply; + +-- Send the "mail from:" command +function mail_from ( + --/** Identify the user who is sending mail. open_connection + -- must have been called previously + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @param email_from The email of the user who is sending mail + -- @return The reply code record from the mailserver. The expected reply + -- code is 250. + -- @see {mail.open_connection}, {mail.rcpt_to}, {mail.send_mail}, + -- Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection, + email_from IN varchar2 +) return utl_smtp.reply; + +-- Send the "rcpt to:" command; forward if neccessary +function rcpt_to ( + --/** Identify the recepient of the email. Automatically forward the + -- mail if the recepient has a new address. mail_from must + -- have been called previously + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @param email_to The email of the recepient of this email + -- @return The reply code record from the mailserver. The expected reply + -- codes are 250 or 251. + -- @see {mail.open_connection}, {mail.open_data}, {mail.send_mail}, + -- Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection, + email_to IN varchar2 +) return utl_smtp.reply; + +-- Open up the data connection, preparing for the writing of data +function open_data ( + --/** Open the data connection, in preparation for sending headers + -- and the body of the message + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @param email_to The email of the recepient of this email + -- @return The reply code record from the mailserver. The expected reply + -- code is 250. + -- @see {mail.open_connection}, {mail.write_data_headers}, {mail.send_mail}, + -- Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection +) return utl_smtp.reply; + +-- Convert a date into email date format (GMT) +function to_email_date ( + --/** Convert an Oracle data into a string date compatible with email (GMT) + -- @author Stanislav Freidin + -- @param ora_date The date to convert + -- @return The converted string date + -- @see {mai.write_data_headers} + --*/ + ora_date IN date +) return varchar2; + +-- Write outgoing headers (date:, to:, from:, subject) +procedure write_data_headers ( + --/** Write the email headers into the mail connection. open_data + -- must have been called previously + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @param email_from The email of sender of this email + -- @param email_to The email of the recepient of this email + -- @param subject A one-line subject for the message + -- @param date_sent The date when the message has been sent + -- @see {mail.open_connection}, {mail.write_data_headers}, + -- {mail.write_data_clob}, {mail.write_data_string}, Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection, + email_from IN varchar2, + email_to IN varchar2, + subject IN varchar2, + date_sent IN date default sysdate +); + +-- Write a clob into the mail data connection, in chunks +procedure write_data_clob ( + --/** Write a clob into the mail data connection, in chunks of 3000 bytes. + -- open_data must have been called prior to this point + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @param clob_loc The clob whose contents will be written into + -- the connection + -- @see {mail.open_connection}, {mail.write_data_headers}, + -- {mail.write_data_string}, {mail.send_mail}, Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection, + clob_loc IN OUT NOCOPY clob +); + +-- Write a string into the mail data connection +procedure write_data_string ( + --/** Write a string into the mail data connection. + -- open_data must have been called prior to this point + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @param string_text The string to be written into the connection + -- @see {mail.open_connection}, {mail.write_data_headers}, + -- {mail.write_data_clob}, {mail.send_mail}, Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection, + string_text IN varchar2 +); + +-- Close the data connection +function close_data ( + --/** Close the data connection after all the text has been written into + -- the body of the message + -- open_data must have been called prior to this point + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @return The reply code record from the mailserver. The expected reply + -- code is 250. + -- @see {mail.open_data}, {mail.close_connection}, Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection +) return utl_smtp.reply; + +-- Close the connection, finish mail session +procedure close_connection ( + --/** Close the mail connection, thus ending the mail sesssion + -- @author Stanislav Freidin + -- @param mail_conn The mail connection object, created with + -- open_connection + -- @see {mail.open_connection}, Oracle's utl_smtp package + --*/ + mail_conn IN OUT NOCOPY utl_smtp.connection +); + +-- A simple procedure to send and entire mail message +-- return 't' on success, 'f' on failure +function send_mail ( + --/** A simplified procedure to send a complete email message + -- @author Stanislav Freidin + -- @param email_from The sender's email + -- @param email_to The recepient's email + -- @param subject A one-line subject to be sent with the message + -- @param text The body of the message, 4000 characters maximum + -- @param host The hostname of the mailserver, such as + -- 'mail.arsdigita.com' + -- @param port The port on which the mailserver expects a connection, + -- default 25 + -- @return 't' if the message was successfully sent, 'f' otherwise + -- @see {mail.open_connection} + --*/ + email_from IN varchar2, + email_to IN varchar2, + subject IN varchar2, + text IN varchar2, + host IN varchar2, + port IN pls_integer default 25 +) return char; + +end mail; +/ +show errors + +create or replace package body mail +is + +function open_connection ( + host IN varchar2, + port IN pls_integer, + mail_conn OUT NOCOPY utl_smtp.connection +) return utl_smtp.reply +is + v_mail_reply utl_smtp.reply; +begin + + v_mail_reply := utl_smtp.open_connection(host, port, mail_conn); + if v_mail_reply.code <> 220 then + return v_mail_reply; + end if; + + return utl_smtp.helo(mail_conn, 'me'); + +end open_connection; + + +function mail_from ( + mail_conn IN OUT NOCOPY utl_smtp.connection, + email_from IN varchar2 +) return utl_smtp.reply +is + v_mail_reply utl_smtp.reply; +begin + + return utl_smtp.command(mail_conn, 'mail from:', email_from); + +end; + + +function rcpt_to ( + mail_conn IN OUT NOCOPY utl_smtp.connection, + email_to IN varchar2 +) return utl_smtp.reply +is + v_mail_reply utl_smtp.reply; + v_email_to varchar2(1000) := email_to; + v_retry_count integer := 0; +begin + + for v_retry_count in 0..20 loop + + begin + v_mail_reply := utl_smtp.command(mail_conn, 'rcpt to:', v_email_to); + + if v_mail_reply.code <> 551 then + return v_mail_reply; + end if; + + -- Got the forwarding line, extract the email address and retry + if v_mail_reply.code = 551 then + declare + v_token_info str.token_info; + v_token varchar2(1000); + v_found char(1); + begin + v_token := str.first_token(v_mail_reply.text, v_token_info); + v_found := 'f'; + + while v_token is not null and v_found = 'f' loop + if instr(v_token, '@') <> 0 then + v_email_to := v_token; + v_found := 't'; + end if; + v_token := str.next_token(v_token_info); + end loop; + + -- If we could not extract the email, failure + if v_found = 'f' then + return v_mail_reply; + end if; + + end; + + end if; + + exception + when others then + return v_mail_reply; + end; + + end loop; + + return v_mail_reply; + +end; + + +function open_data ( + mail_conn IN OUT NOCOPY utl_smtp.connection +) return utl_smtp.reply +is +begin + return utl_smtp.open_data(mail_conn); +end open_data; + + +function to_email_date ( + ora_date IN date +) return varchar2 +is +begin + return initcap(to_char(ora_date, 'DY, DD MON YYYY HH24:MI:SS')); +end to_email_date; + +procedure write_data_headers ( + mail_conn IN OUT NOCOPY utl_smtp.connection, + email_from IN varchar2, + email_to IN varchar2, + subject IN varchar2, + date_sent IN date default sysdate +) +is + v_newline varchar2(20) := ' +'; +begin + utl_smtp.write_data(mail_conn, + 'Date: ' || to_email_date(date_sent) || v_newline || + 'From: ' || email_from || v_newline || + 'To: ' || email_to || v_newline || + 'Subject:' || subject || v_newline || + 'Content-type: text/plain' || v_newline || + v_newline + ); +end write_data_headers; + + +procedure write_data_clob ( + mail_conn IN OUT NOCOPY utl_smtp.connection, + clob_loc IN OUT NOCOPY clob +) +is + v_clob_length integer; + v_string varchar2(4000); + v_reply utl_smtp.reply; + v_offset integer; + v_amount integer; +begin + + v_clob_length := dbms_lob.getlength(clob_loc); + v_offset := 1; + + while v_clob_length > 0 loop + if v_clob_length < 3000 then + v_amount := v_clob_length; + else + v_amount := 3000; + end if; + + dbms_lob.read(clob_loc, v_amount, v_offset, v_string); + utl_smtp.write_data(mail_conn, v_string); + + v_clob_length := v_clob_length - 3000; + v_offset := v_offset + 3000; + end loop; +end write_data_clob; + +-- Write a string into the mail data connection +procedure write_data_string ( + mail_conn IN OUT NOCOPY utl_smtp.connection, + string_text IN varchar2 +) +is +begin + utl_smtp.write_data(mail_conn, string_text); +end write_data_string; + +function close_data ( + mail_conn IN OUT NOCOPY utl_smtp.connection +) return utl_smtp.reply +is +begin + return utl_smtp.close_data(mail_conn); +end close_data; + + +procedure close_connection ( + mail_conn IN OUT NOCOPY utl_smtp.connection +) +is +begin + utl_smtp.quit(mail_conn); +end close_connection; + +-- A simple procedure to send and entire mail message +function send_mail ( + email_from IN varchar2, + email_to IN varchar2, + subject IN varchar2, + text IN varchar2, + host IN varchar2, + port IN pls_integer default 25 +) return char +is + v_reply utl_smtp.reply; + v_mail_conn utl_smtp.connection; +begin + + v_reply := open_connection(host, port, v_mail_conn); + if v_reply.code <> 250 then + return 'f'; + end if; + + v_reply := mail_from(v_mail_conn, email_from); + if v_reply.code <> 250 then + return 'f'; + end if; + + v_reply := rcpt_to(v_mail_conn, email_to); + if v_reply.code not in (250, 251) then + return 'f'; + end if; + + v_reply := open_data(v_mail_conn); + write_data_headers( + v_mail_conn, email_from, email_to, subject, sysdate + ); + write_data_string(v_mail_conn, text); + v_reply := close_data(v_mail_conn); + if v_reply.code <> 250 then + return 'f'; + end if; + + close_connection(v_mail_conn); + + return 't'; +end send_mail; + +end mail; +/ +show errors + + + + + + + Index: openacs-4/packages/acs-subsite/acs-subsite.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/acs-subsite.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-subsite/acs-subsite.info 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-subsite/acs-subsite.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ f - - oracle-8.1.6 - + + oracle + postgresql + Michael Yoon Rafael Schloming Oumi Mehrotra @@ -26,21 +27,21 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Index: openacs-4/packages/acs-subsite/sql/oracle/acs-subsite-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/acs-subsite-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/acs-subsite-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,29 @@ +-- Create the necessary data model and ACS relationships for the ACS Core UI. +-- +-- @author Hiro Iwashima (iwashima@mit.edu) +-- +-- @creation-date 28 August 2000 +-- +-- @cvs-id $Id: acs-subsite-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +@@ attribute +@@ portraits +@@ application-groups-create +@@ subsite-callbacks-create + +-- This view lets us avoid using acs_object.name to get party_names. +-- +create or replace view party_names +as +select p.party_id, + decode(groups.group_id, + null, decode(persons.person_id, + null, p.email, + persons.first_names || ' ' || persons.last_name), + groups.group_name) as party_name +from parties p, + groups, + persons +where p.party_id = groups.group_id(+) + and p.party_id = persons.person_id(+); Index: openacs-4/packages/acs-subsite/sql/oracle/acs-subsite-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/acs-subsite-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/acs-subsite-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,12 @@ +-- Uninstall file for the data model created by 'acs-core-ui-create.sql' +-- (This file created automatically by create-sql-uninst.pl.) +-- +-- @author Bryan Quinn +-- @creation-date (Sat Aug 26 17:56:07 2000) +-- @cvs-id $Id: acs-subsite-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +@@ subsite-group-callbacks-drop +@@ application-groups-drop +@@ user-profiles-drop +@@ attributes-drop +@@ portraits-drop Index: openacs-4/packages/acs-subsite/sql/oracle/application-groups-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/application-groups-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/application-groups-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,195 @@ +-- +-- packages/acs-subsite/sql/application_groups-create.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: application-groups-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +------------------------ +-- APPLICATION GROUPS -- +------------------------ + +begin + acs_object_type.create_type ( + supertype => 'group', + object_type => 'application_group', + pretty_name => 'Application Group', + pretty_plural => 'Application Groups', + table_name => 'application_groups', + id_column => 'group_id', + package_name => 'application_group', + type_extension_table => 'group_types', + name_method => 'acs_group.name' + ); +end; +/ +show errors + +create table application_groups ( + group_id constraint app_groups_group_id_fk + references groups (group_id) + constraint app_groups_group_id_pk + primary key, + package_id constraint app_groups_package_id_fk + references apm_packages, + constraint app_groups_package_id_un + unique (package_id) +); + + +create or replace package application_group +is + + function new ( + group_id in application_groups.group_id%TYPE default null, + object_type in acs_objects.object_type%TYPE + default 'application_group', + creation_date in acs_objects.creation_date%TYPE + default sysdate, + creation_user in acs_objects.creation_user%TYPE + default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + email in parties.email%TYPE default null, + url in parties.url%TYPE default null, + group_name in groups.group_name%TYPE, + package_id in application_groups.package_id%TYPE, + context_id in acs_objects.context_id%TYPE default null + ) return application_groups.group_id%TYPE; + + procedure delete ( + group_id in application_groups.group_id%TYPE + ); + + function group_id_from_package_id ( + package_id in application_groups.group_id%TYPE, + no_complain_p in char default 'f' + ) return char; + +end application_group; +/ +show errors + + +create or replace package body application_group +is + + function new ( + group_id in application_groups.group_id%TYPE default null, + object_type in acs_objects.object_type%TYPE + default 'application_group', + creation_date in acs_objects.creation_date%TYPE + default sysdate, + creation_user in acs_objects.creation_user%TYPE + default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + email in parties.email%TYPE default null, + url in parties.url%TYPE default null, + group_name in groups.group_name%TYPE, + package_id in application_groups.package_id%TYPE, + context_id in acs_objects.context_id%TYPE default null + ) + return application_groups.group_id%TYPE + is + v_group_id application_groups.group_id%TYPE; + begin + v_group_id := acs_group.new ( + group_id => group_id, + object_type => object_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + email => email, + url => url, + group_name => group_name, + context_id => context_id + ); + + insert into application_groups (group_id, package_id) + values (v_group_id, package_id); + + return v_group_id; + end new; + + + procedure delete ( + group_id in application_groups.group_id%TYPE + ) + is + begin + + acs_group.delete(group_id); + + end delete; + + function group_id_from_package_id ( + package_id in application_groups.group_id%TYPE, + no_complain_p in char default 'f' + ) return char + is + v_group_id application_groups.group_id%TYPE; + begin + + select group_id + into v_group_id + from application_groups + where package_id = group_id_from_package_id.package_id; + + return v_group_id; + + exception when no_data_found then + + if no_complain_p != 't' then + raise_application_error(-20000, 'No group_id found for package ' || + package_id || ' (' || acs_object.name(package_id) || ').' ); + end if; + + return null; + + end group_id_from_package_id; + +end application_group; +/ +show errors + +insert into group_type_rels +(group_rel_type_id, group_type, rel_type) +values +(acs_object_id_seq.nextval, 'application_group', 'composition_rel'); + +insert into group_type_rels +(group_rel_type_id, group_type, rel_type) +values +(acs_object_id_seq.nextval, 'application_group', 'membership_rel'); + +----------- +-- Views -- +----------- + +create or replace view application_group_element_map as +select g.package_id, g.group_id, + m.element_id, m.container_id, m.rel_id, m.rel_type, m.ancestor_rel_type +from application_groups g, + group_element_map m +where g.group_id = m.group_id; + +create or replace view app_group_distinct_element_map as +select distinct package_id, group_id, element_id +from application_group_element_map; + +create or replace view app_group_distinct_rel_map as +select distinct package_id, group_id, rel_id, rel_type, ancestor_rel_type +from application_group_element_map; + +create or replace view application_group_segments as +select g.package_id, s.segment_id, s.group_id, s.rel_type, s.segment_name +from application_groups g, + group_element_map m, + rel_segments s +where g.group_id = m.group_id + and m.element_id = s.group_id +UNION ALL +select g.package_id, s.segment_id, s.group_id, s.rel_type, s.segment_name +from application_groups g, + rel_segments s +where g.group_id = s.group_id; Index: openacs-4/packages/acs-subsite/sql/oracle/application-groups-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/application-groups-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/application-groups-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,23 @@ +-- +-- packages/acs-subsite/sql/application-groups-drop.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: application-groups-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +delete from group_type_rels where rel_type = 'application_group'; + +drop table application_groups; +drop package application_group; + +begin + acs_object_type.drop_type('application_group'); +end; +/ +show errors + +drop view application_group_element_map; +drop view application_users; +drop view registered_users_for_package_id; +drop view cc_users_for_package_id; \ No newline at end of file Index: openacs-4/packages/acs-subsite/sql/oracle/attribute.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/Attic/attribute.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/attribute.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,30 @@ +-- +-- acs-core-ui/sql/attribute-create.sql +-- +-- Creates the necessary attributes for objects for the core ui +-- +-- @author Hiro Iwashima (iwashima@mit.edu) +-- +-- @creation-date 18 May 2000 +-- +-- @cvs-id $Id: attribute.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +declare + result varchar2(10); +begin + result := acs_attribute.create_attribute ( + object_type => 'person', + attribute_name => 'bio', + datatype => 'string', + pretty_name => 'Biography', + pretty_plural => 'Biographies', + min_n_values => 0, + max_n_values => 1, + storage => 'generic' + ); + + commit; +end; +/ +show errors Index: openacs-4/packages/acs-subsite/sql/oracle/attributes-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/Attic/attributes-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/attributes-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,27 @@ +-- +-- packages/acs-subsite/sql/attributes-drop.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: attributes-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- +-- + +declare + result varchar2(10); +begin + result := acs_attribute.create_attribute ( + object_type => 'person', + attribute_name => 'bio', + datatype => 'string', + pretty_name => 'Biography', + pretty_plural => 'Biographies', + min_n_values => 0, + max_n_values => 1, + storage => 'generic' + ); + + commit; +end; +/ +show errors Index: openacs-4/packages/acs-subsite/sql/oracle/portraits-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/portraits-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/portraits-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,16 @@ +-- +-- packages/acs-subsite/sql/portraits-drop.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: portraits-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +drop table user_portraits; +drop package user_portrait_rel; + +begin + acs_rel_type.drop_type('user_portrait_rel'); +end; +/ +show errors Index: openacs-4/packages/acs-subsite/sql/oracle/portraits.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/portraits.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/portraits.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,38 @@ +-- Portrait Data Model + +-- Copyright (C) 1999-2000 ArsDigita Corporation +-- Author: Hiro Iwashima (iwashima@mit.edu) + +-- $Id: portraits.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +create table user_portraits ( + user_id constraint user_portraits_user_id_fk + references users + constraint user_portraits_pk + primary key +); + +begin + acs_rel_type.create_role('user', 'User', 'Users'); + acs_rel_type.create_role('portrait', 'Portrait', 'Portraits'); + + acs_rel_type.create_type ( + rel_type => 'user_portrait_rel', + pretty_name => 'User Portrait', + pretty_plural => 'User Portraits', + object_type_one => 'user', + role_one => 'user', + table_name => 'user_portraits', + id_column => 'user_id', + package_name => 'user_portrait_rel', + min_n_rels_one => 1, + max_n_rels_one => 1, + object_type_two => 'content_item', + min_n_rels_two => 0, + max_n_rels_two => 1 + ); + + commit; +end; +/ +show errors Index: openacs-4/packages/acs-subsite/sql/oracle/subsite-callbacks-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/subsite-callbacks-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/subsite-callbacks-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,178 @@ +-- /packages/acs-subsite/sql/subsite-group-callbacks-create.sql + +-- Defines a simple callback system to allow other applications to +-- register callbacks when groups of a given type are created. + +-- Copyright (C) 2001 ArsDigita Corporation +-- @author Michael Bryzek (mbryzek@arsdigita.com) +-- @creation-date 2001-02-20 + +-- $Id: subsite-callbacks-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +-- 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 + + +-- What about instead of? + -- insead_of viewing the group, go to the portal + -- instead of inserting the group with package_instantiate_object, go here + +create table subsite_callbacks ( + callback_id integer + constraint sgc_callback_id_pk primary key, + event_type varchar(100) not null + constraint sgc_event_type_ck check(event_type in ('insert','update','delete')), + object_type varchar(100) not null + constraint sgc_object_type_fk references acs_object_types + on delete cascade, + callback varchar(300) not null, + callback_type varchar(100) not null + constraint sgc_callback_type_ck check(callback_type in ('tcl')), + sort_order integer default(1) not null + constraint sgc_sort_order_ck check(sort_order >= 1), + -- allow only one callback of a given type for given + constraint subsite_callbacks_un unique (object_type, event_type, callback_type, callback) +); + +comment on table subsite_callbacks is ' + Applications can register callbacks that are triggered + whenever a group of a specified type is created. The callback + must expect the following arguments: + * object_id: The object that just got created + * node_id: The node_id where the object got created + * package_id: The package_id from where the object got created + These are passed in the following way: + * tcl procedure: Using named parameters (e.g. -object_id $object_id) + All callbacks must accept all of these parameters. +'; + +comment on column subsite_callbacks.event_type is ' + The type of event we are monitoring. The keywords here are used + by the applications to determine which callbacks to trigger. +'; + +comment on column subsite_callbacks.object_type is ' + The object type to monitor. Whenever an object of this type is + created, the subsite package will check for a registered + callbacks. +'; + +comment on column subsite_callbacks.callback_type is ' + The type of the callback. This determines how the callback is + executed. Currenlty only a tcl type is supported but other + types may be added in the future. +'; + + +comment on column subsite_callbacks.callback is ' + The actual callback. This can be the name of a plsql function + or procedure, a url stub relative to the node at which package + id is mounted, or the name of a tcl function. +'; + +comment on column subsite_callbacks.sort_order is ' + The order in which the callbacks should fire. This is + important when you need to ensure that one event fires before + another (e.g. you must mount a portals application before the + bboard application) +'; + + +create or replace package subsite_callback as + + function new ( + --/** Registers a new callback. If the same callback exists as + -- defined in the unique constraint on the table, does + -- nothing but returns the existing callback_id. + -- + -- @author Michael Bryzek (mbryzek@arsdigita.com) + -- @creation-date 2001-02-20 + -- + --*/ + callback_id IN subsite_callbacks.callback_id%TYPE default null, + event_type IN subsite_callbacks.event_type%TYPE, + object_type IN subsite_callbacks.object_type%TYPE, + callback IN subsite_callbacks.callback%TYPE, + callback_type IN subsite_callbacks.callback_type%TYPE, + sort_order IN subsite_callbacks.sort_order%TYPE default null + ) return subsite_callbacks.callback_id%TYPE; + + procedure delete ( + --/** Deletes the specified callback + -- + -- @author Michael Bryzek (mbryzek@arsdigita.com) + -- @creation-date 2001-02-20 + -- + --*/ + + callback_id IN subsite_callbacks.callback_id%TYPE + ); + +end subsite_callback; +/ +show errors; + + + +create or replace package body subsite_callback as + + function new ( + callback_id IN subsite_callbacks.callback_id%TYPE default null, + event_type IN subsite_callbacks.event_type%TYPE, + object_type IN subsite_callbacks.object_type%TYPE, + callback IN subsite_callbacks.callback%TYPE, + callback_type IN subsite_callbacks.callback_type%TYPE, + sort_order IN subsite_callbacks.sort_order%TYPE default null + ) return subsite_callbacks.callback_id%TYPE + IS + v_callback_id subsite_callbacks.callback_id%TYPE; + v_sort_order subsite_callbacks.sort_order%TYPE; + BEGIN + + if new.callback_id is null then + select acs_object_id_seq.nextval into v_callback_id from dual; + else + v_callback_id := new.callback_id; + end if; + + if new.sort_order is null then + -- Make this the next event for this object_type/event_type combination + select nvl(max(sort_order),0) + 1 into v_sort_order + from subsite_callbacks + where object_type = new.object_type + and event_type = new.event_type; + else + v_sort_order := new.sort_order; + end if; + + begin + insert into subsite_callbacks + (callback_id, event_type, object_type, callback, callback_type, sort_order) + values + (v_callback_id, new.event_type, new.object_type, new.callback, new.callback_type, v_sort_order); + exception when dup_val_on_index then + select callback_id into v_callback_id + from subsite_callbacks + where event_type = new.event_type + and object_type = new.object_type + and callback_type = new.callback_type + and callback = new.callback; + end; + return v_callback_id; + + END new; + + + procedure delete ( + callback_id IN subsite_callbacks.callback_id%TYPE + ) + is + begin + delete from subsite_callbacks where callback_id=subsite_callback.delete.callback_id; + end delete; + +end subsite_callback; +/ +show errors; + Index: openacs-4/packages/acs-subsite/sql/oracle/subsite-callbacks-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/subsite-callbacks-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/subsite-callbacks-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,16 @@ +-- /packages/acs-subsite/sql/subsite-group-callbacks-drop.sql + +-- Drops the subsite group callbacks data model + +-- Copyright (C) 2001 ArsDigita Corporation +-- @author Michael Bryzek (mbryzek@arsdigita.com) +-- @creation-date 2001-02-21 + +-- $Id: subsite-callbacks-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +-- 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 package subsite_callback; +drop table subsite_callbacks; Index: openacs-4/packages/acs-subsite/sql/oracle/user-profiles-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/user-profiles-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/user-profiles-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,151 @@ +-- +-- packages/acs-subsite/sql/user-profiles-create.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: user-profiles-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +------------------------------- +-- APPLICATION USER PROFILES -- +------------------------------- + +begin + + -- the 'user' role should already exist from the portraits stuff. + -- acs_rel_type.create_role('user', + -- 'Registered User', 'Registered Users'); + + acs_rel_type.create_role('application', + 'Application Group', 'Application Group'); + + acs_rel_type.create_type( + rel_type => 'user_profile', + pretty_name => 'User Profile', + pretty_plural => 'User Profiles', + supertype => 'membership_rel', + table_name => 'user_profiles', + id_column => 'profile_id', + package_name => 'user_profile', + abstract_p => 'f', + object_type_one => 'application_group', + role_one => 'application', + min_n_rels_one => 0, + max_n_rels_one => null, + object_type_two => 'user', + role_two => 'user', + min_n_rels_two => 0, + max_n_rels_two => null + ); + +end; +/ +show errors + + +create table user_profiles ( + profile_id constraint user_profiles_profile_id_fk + references membership_rels (rel_id) + constraint user_profiles_profile_id_pk + primary key +); + + +create or replace package user_profile +as + + function new ( + profile_id in user_profiles.profile_id%TYPE default null, + rel_type in acs_rels.rel_type%TYPE default 'user_profile', + object_id_one in acs_rels.object_id_one%TYPE, + object_id_two in acs_rels.object_id_two%TYPE, + member_state in membership_rels.member_state%TYPE default null, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null + ) return user_profiles.profile_id%TYPE; + + procedure delete ( + profile_id in user_profiles.profile_id%TYPE + ); + +end user_profile; +/ +show errors + + +create or replace package body user_profile +as + + function new ( + profile_id in user_profiles.profile_id%TYPE default null, + rel_type in acs_rels.rel_type%TYPE default 'user_profile', + object_id_one in acs_rels.object_id_one%TYPE, + object_id_two in acs_rels.object_id_two%TYPE, + member_state in membership_rels.member_state%TYPE default null, + creation_user in acs_objects.creation_user%TYPE default null, + creation_ip in acs_objects.creation_ip%TYPE default null + ) return user_profiles.profile_id%TYPE + is + v_profile_id integer; + begin + + v_profile_id := membership_rel.new ( + rel_id => profile_id, + rel_type => rel_type, + object_id_one => object_id_one, + object_id_two => object_id_two, + member_state => member_state, + creation_user => creation_user, + creation_ip => creation_ip + ); + + insert into user_profiles (profile_id) values (v_profile_id); + + return v_profile_id; + end new; + + procedure delete ( + profile_id in user_profiles.profile_id%TYPE + ) + is + begin + + membership_rel.delete(profile_id); + + end delete; + +end user_profile; +/ +show errors + +insert into group_type_rels +(group_rel_type_id, group_type, rel_type) +values +(acs_object_id_seq.nextval, 'application_group', 'user_profile'); + + +-- This view is extremely fast, but for some reason its not so blaxing fast +-- when used in the registered_users_of_package_id view below. +create or replace view application_users as + select ag.package_id, gem.element_id as user_id + from user_profiles up, + group_element_map gem, + application_groups ag + where ag.group_id = gem.group_id + and gem.rel_id = up.profile_id; + + +-- create the generalized versions of the registered_users and cc_users views: + +create or replace view registered_users_of_package_id as + select u.*, au.package_id + from application_users au, + registered_users u + where au.user_id = u.user_id; + +create or replace view cc_users_of_package_id as + select u.*, au.package_id + from application_users au, + cc_users u + where au.user_id = u.user_id; + Index: openacs-4/packages/acs-subsite/sql/oracle/user-profiles-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/user-profiles-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/user-profiles-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,17 @@ +-- +-- packages/acs-subsite/sql/user-profiles-drop.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: user-profiles-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +drop table user_profiles; +drop package user_profile; + +begin + acs_rel_type.drop_type('user_profile'); +end; +/ +show errors + Index: openacs-4/packages/acs-subsite/sql/oracle/tests/acs-subsite-test-harness.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/tests/acs-subsite-test-harness.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/tests/acs-subsite-test-harness.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,16 @@ +-- /packages/acs-subsite/sql/tests/acs-subsite-test-harness.sql + +-- Test harness to run all the tests in this directory. New tests +-- should be added to this file + +-- Copyright (C) 2001 ArsDigita Corporation +-- @author Michael Bryzek (mbryzek@arsdigita.com) +-- @creation-date 2001-02-20 + +-- $Id: acs-subsite-test-harness.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +-- 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 + +@@ subsite-callbacks-test Index: openacs-4/packages/acs-subsite/sql/oracle/tests/subsite-callbacks-test.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/tests/subsite-callbacks-test.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/tests/subsite-callbacks-test.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,74 @@ +-- /packages/acs-subsite/sql/tests/subsite-group-callbacks-test.sql + +-- Test the basic API to the subsite_callback package. You will +-- get an application error if there is an error + +-- Copyright (C) 2001 ArsDigita Corporation +-- @author Michael Bryzek (mbryzek@arsdigita.com) +-- @creation-date 2001-02-20 + +-- $Id: subsite-callbacks-test.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +-- 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 + +declare + v_count integer; + v_callback_id integer; + v_node_id integer; +begin + select min(node_id) into v_node_id from site_nodes; + + for i in 0..2 loop + v_callback_id := subsite_callback.new(event_type=>'insert', + object_type=>'group', + callback=>'subsite_callback_test_foo', + callback_type=>'tcl' + ); + end loop; + + select count(*) into v_count + from subsite_callbacks + where object_type = 'group' + and event_type = 'insert' + and callback_type = 'tcl' + and callback = 'subsite_callback_test_foo'; + + if v_count = 0 then + raise_application_error(-20000,'Insert failed'); + elsif v_count > 1 then + raise_application_error(-20000,'Duplicate insert succeeded where it should have done nothing.'); + end if; + + subsite_callback.delete(v_callback_id); + + v_callback_id := subsite_callback.new(object_type=>'group', + event_type=>'insert', + callback=>'subsite_callback_test_foo2', + callback_type=>'tcl'); + + select count(*) into v_count + from subsite_callbacks + where object_type = 'group' + and callback = 'subsite_callback_test_foo2' + and callback_type = 'tcl'; + + if v_count = 0 then + raise_application_error(-20000,'Insert failed'); + end if; + + subsite_callback.delete(v_callback_id); + + select count(*) into v_count + from subsite_callbacks + where callback in ('subsite_callback_test_foo','subsite_callback_test_foo2'); + + if v_count > 0 then + raise_application_error(-20000,'Delete failed'); + end if; + +end; +/ +show errors; + Index: openacs-4/packages/acs-subsite/sql/oracle/upgrade/upgrade-4.1.1-4.2.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-subsite/sql/oracle/upgrade/upgrade-4.1.1-4.2.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-subsite/sql/oracle/upgrade/upgrade-4.1.1-4.2.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,525 @@ + +-- before release, we'll have to copy and paste from the referenced sql +-- files into this one. For now, we just reference some sql files. + +------------------------------------------------------------------------------ +-- packages/acs-subsite/sql/application_groups-create.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: upgrade-4.1.1-4.2.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +------------------------ +-- APPLICATION GROUPS -- +------------------------ + + +begin + acs_object_type.create_type ( + supertype => 'group', + object_type => 'application_group', + pretty_name => 'Application Group', + pretty_plural => 'Application Groups', + table_name => 'application_groups', + id_column => 'group_id', + package_name => 'application_group', + type_extension_table => 'group_types', + name_method => 'acs_group.name' + ); +end; +/ +show errors + +create table application_groups ( + group_id constraint app_groups_group_id_fk + references groups (group_id) + constraint app_groups_group_id_pk + primary key, + package_id constraint app_groups_package_id_fk + references apm_packages, + constraint app_groups_package_id_un + unique (package_id) +); + + +create or replace package application_group +is + + function new ( + group_id in application_groups.group_id%TYPE default null, + object_type in acs_objects.object_type%TYPE + default 'application_group', + creation_date in acs_objects.creation_date%TYPE + default sysdate, + creation_user in acs_objects.creation_user%TYPE + default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + email in parties.email%TYPE default null, + url in parties.url%TYPE default null, + group_name in groups.group_name%TYPE, + package_id in application_groups.package_id%TYPE, + context_id in acs_objects.context_id%TYPE default null + ) return application_groups.group_id%TYPE; + + procedure delete ( + group_id in application_groups.group_id%TYPE + ); + + function group_id_from_package_id ( + package_id in application_groups.group_id%TYPE, + no_complain_p in char default 'f' + ) return char; + +end application_group; +/ +show errors + + +create or replace package body application_group +is + + function new ( + group_id in application_groups.group_id%TYPE default null, + object_type in acs_objects.object_type%TYPE + default 'application_group', + creation_date in acs_objects.creation_date%TYPE + default sysdate, + creation_user in acs_objects.creation_user%TYPE + default null, + creation_ip in acs_objects.creation_ip%TYPE default null, + email in parties.email%TYPE default null, + url in parties.url%TYPE default null, + group_name in groups.group_name%TYPE, + package_id in application_groups.package_id%TYPE, + context_id in acs_objects.context_id%TYPE default null + ) + return application_groups.group_id%TYPE + is + v_group_id application_groups.group_id%TYPE; + begin + v_group_id := acs_group.new ( + group_id => group_id, + object_type => object_type, + creation_date => creation_date, + creation_user => creation_user, + creation_ip => creation_ip, + email => email, + url => url, + group_name => group_name, + context_id => context_id + ); + + insert into application_groups (group_id, package_id) + values (v_group_id, package_id); + + return v_group_id; + end new; + + + procedure delete ( + group_id in application_groups.group_id%TYPE + ) + is + begin + + acs_group.delete(group_id); + + end delete; + + function group_id_from_package_id ( + package_id in application_groups.group_id%TYPE, + no_complain_p in char default 'f' + ) return char + is + v_group_id application_groups.group_id%TYPE; + begin + + select group_id + into v_group_id + from application_groups + where package_id = group_id_from_package_id.package_id; + + return v_group_id; + + exception when no_data_found then + + if no_complain_p != 't' then + raise_application_error(-20000, 'No group_id found for package ' || + package_id || ' (' || acs_object.name(package_id) || ').' ); + end if; + + return null; + + end group_id_from_package_id; + +end application_group; +/ +show errors + +insert into group_type_rels +(group_rel_type_id, group_type, rel_type) +values +(acs_object_id_seq.nextval, 'application_group', 'composition_rel'); + +insert into group_type_rels +(group_rel_type_id, group_type, rel_type) +values +(acs_object_id_seq.nextval, 'application_group', 'membership_rel'); + +----------- +-- Views -- +----------- + +create or replace view application_group_element_map as +select g.package_id, g.group_id, + m.element_id, m.container_id, m.rel_id, m.rel_type, m.ancestor_rel_type +from application_groups g, + group_element_map m +where g.group_id = m.group_id; + +create or replace view app_group_distinct_element_map as +select distinct package_id, group_id, element_id +from application_group_element_map; + +create or replace view app_group_distinct_rel_map as +select distinct package_id, group_id, rel_id, rel_type, ancestor_rel_type +from application_group_element_map; + +create or replace view application_group_segments as +select g.package_id, s.segment_id, s.group_id, s.rel_type, s.segment_name +from application_groups g, + group_element_map m, + rel_segments s +where g.group_id = m.group_id + and m.element_id = s.group_id +UNION ALL +select g.package_id, s.segment_id, s.group_id, s.rel_type, s.segment_name +from application_groups g, + rel_segments s +where g.group_id = s.group_id; + + +------------------------------------------------------------------------------ +-- packages/acs-subsite/sql/user-profiles-create.sql +-- +-- @author oumi@arsdigita.com +-- @creation-date 2000-02-02 +-- @cvs-id $Id: upgrade-4.1.1-4.2.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +--------------------------- +-- UPGRADE EXISTING DATA -- +--------------------------- + +-- ACS's current system: +-- +-- - Magic object -2 is the 'Registered Users' party. +-- +-- - developers use the views registered_users and cc_registered_users. +-- These views join the users table with the members of group -2. +-- +-- ACS Subsite 4.1.2 now adds a concept of users (or any party, for that +-- matter) "belonging" to a subsite. The upgrade to 4.1.2 needs to +-- add all registered users to the main site. +-- +-- In future versions of ACS, the registration stuff should get RIPPED OUT +-- of the kernel (Rafi agrees). Right now, we take the path of least change. +-- +-- The new and improved system: +-- +-- - a group type called 'application_group' is created. Application groups +-- have a package_id. The application group serves as a container for +-- all parties that belong to the package_id application instance. +-- (see application-groups-create.sql) +-- +-- - An application group called 'Main Site Parties' is created. Its +-- package_id points to the main site. +-- + + +-- Assume that application-groups-create has already been run. + +set serveroutput on; + +declare + v_package_id integer; + v_group_name varchar(100); + v_group_id integer; + v_rel_id integer; + v_segment_id integer; + v_segment_name varchar(100); +begin + + dbms_output.put_line('selecting main site instance name and package_id'); + + select package_id, + substr(instance_name, 1, 90) || ' Parties', + substr(instance_name, 1, 60) || ' Registered Users' + into v_package_id, v_group_name, v_segment_name + from apm_packages, site_nodes + where site_nodes.object_id = apm_packages.package_id + and site_nodes.parent_id is null; + + dbms_output.put_line('creating main site application_group'); + + v_group_id := application_group.new( + group_name => v_group_name, + package_id => v_package_id + ); + + dbms_output.put_line('adding system users to main site'); + + for r in (select user_id, mr.member_state + from users, membership_rels mr, acs_rels r + where user_id = r.object_id_two and object_id_one = -2 + and r.rel_id = mr.rel_id ) loop + + v_rel_id := membership_rel.new ( + object_id_one => v_group_id, + object_id_two => r.user_id, + member_state => r.member_state + ); + + end loop; + + -- add all the groups in the system to the Main Site Parties group + -- (except for 'Registered Users' and 'Main Site Parties' itself) + for r in (select group_id + from groups + where not exists(select 1 from group_component_map + where group_id = groups.group_id) + and group_id not in (-2, v_group_id)) loop + + v_rel_id := composition_rel.new ( + object_id_one => v_group_id, + object_id_two => r.group_id + ); + + end loop; + + -- add the 'Main Site Registered Members' segment: + v_segment_id := rel_segment.new( + segment_name=> v_segment_name, + group_id => v_group_id, + rel_type => 'membership_rel' + ); + +end; +/ +show errors + +-------------------------------------------------------------- +-- acs-subsite-create.sql +-- oumi@arsdigita.com +-- 2/20/2001 +-- +-- CHANGES +-- +-- Added party_names view. +-------------------------------------------------------------- + +-- This view lets us avoid using acs_object.name to get party_names. +-- +create or replace view party_names +as +select p.party_id, + decode(groups.group_id, + null, decode(persons.person_id, + null, p.email, + persons.first_names || ' ' || persons.last_name), + groups.group_name) as party_name +from parties p, + groups, + persons +where p.party_id = groups.group_id(+) + and p.party_id = persons.person_id(+); + + + + +-------------------------------------------------------------- +-- subsite-callbacks-create.sql +-- mbryzek@arsdigita.com +-- 2/20/2001 +-------------------------------------------------------------- + +-- /packages/acs-subsite/sql/subsite-group-callbacks-create.sql + +-- Defines a simple callback system to allow other applications to +-- register callbacks when groups of a given type are created. + +-- Copyright (C) 2001 ArsDigita Corporation +-- @author Michael Bryzek (mbryzek@arsdigita.com) +-- @creation-date 2001-02-20 + +-- $Id: upgrade-4.1.1-4.2.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +-- 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 + + +-- What about instead of? + -- insead_of viewing the group, go to the portal + -- instead of inserting the group with package_instantiate_object, go here + +create table subsite_callbacks ( + callback_id integer + constraint sgc_callback_id_pk primary key, + event_type varchar(100) not null + constraint sgc_event_type_ck check(event_type in ('insert','update','delete')), + object_type varchar(100) not null + constraint sgc_object_type_fk references acs_object_types + on delete cascade, + callback varchar(300) not null, + callback_type varchar(100) not null + constraint sgc_callback_type_ck check(callback_type in ('tcl')), + sort_order integer default(1) not null + constraint sgc_sort_order_ck check(sort_order >= 1), + -- allow only one callback of a given type for given + constraint subsite_callbacks_un unique (object_type, event_type, callback_type, callback) +); + +comment on table subsite_callbacks is ' + Applications can register callbacks that are triggered + whenever a group of a specified type is created. The callback + must expect the following arguments: + * object_id: The object that just got created + * node_id: The node_id where the object got created + * package_id: The package_id from where the object got created + These are passed in the following way: + * tcl procedure: Using named parameters (e.g. -object_id $object_id) + All callbacks must accept all of these parameters. +'; + +comment on column subsite_callbacks.event_type is ' + The type of event we are monitoring. The keywords here are used + by the applications to determine which callbacks to trigger. +'; + +comment on column subsite_callbacks.object_type is ' + The object type to monitor. Whenever an object of this type is + created, the subsite package will check for a registered + callbacks. +'; + +comment on column subsite_callbacks.callback_type is ' + The type of the callback. This determines how the callback is + executed. Currenlty only a tcl type is supported but other + types may be added in the future. +'; + + +comment on column subsite_callbacks.callback is ' + The actual callback. This can be the name of a plsql function + or procedure, a url stub relative to the node at which package + id is mounted, or the name of a tcl function. +'; + +comment on column subsite_callbacks.sort_order is ' + The order in which the callbacks should fire. This is + important when you need to ensure that one event fires before + another (e.g. you must mount a portals application before the + bboard application) +'; + + +create or replace package subsite_callback as + + function new ( + --/** Registers a new callback. If the same callback exists as + -- defined in the unique constraint on the table, does + -- nothing but returns the existing callback_id. + -- + -- @author Michael Bryzek (mbryzek@arsdigita.com) + -- @creation-date 2001-02-20 + -- + --*/ + callback_id IN subsite_callbacks.callback_id%TYPE default null, + event_type IN subsite_callbacks.event_type%TYPE, + object_type IN subsite_callbacks.object_type%TYPE, + callback IN subsite_callbacks.callback%TYPE, + callback_type IN subsite_callbacks.callback_type%TYPE, + sort_order IN subsite_callbacks.sort_order%TYPE default null + ) return subsite_callbacks.callback_id%TYPE; + + procedure delete ( + --/** Deletes the specified callback + -- + -- @author Michael Bryzek (mbryzek@arsdigita.com) + -- @creation-date 2001-02-20 + -- + --*/ + + callback_id IN subsite_callbacks.callback_id%TYPE + ); + +end subsite_callback; +/ +show errors; + + + +create or replace package body subsite_callback as + + function new ( + callback_id IN subsite_callbacks.callback_id%TYPE default null, + event_type IN subsite_callbacks.event_type%TYPE, + object_type IN subsite_callbacks.object_type%TYPE, + callback IN subsite_callbacks.callback%TYPE, + callback_type IN subsite_callbacks.callback_type%TYPE, + sort_order IN subsite_callbacks.sort_order%TYPE default null + ) return subsite_callbacks.callback_id%TYPE + IS + v_callback_id subsite_callbacks.callback_id%TYPE; + v_sort_order subsite_callbacks.sort_order%TYPE; + BEGIN + + if new.callback_id is null then + select acs_object_id_seq.nextval into v_callback_id from dual; + else + v_callback_id := new.callback_id; + end if; + + if new.sort_order is null then + -- Make this the next event for this object_type/event_type combination + select nvl(max(sort_order),0) + 1 into v_sort_order + from subsite_callbacks + where object_type = new.object_type + and event_type = new.event_type; + else + v_sort_order := new.sort_order; + end if; + + begin + insert into subsite_callbacks + (callback_id, event_type, object_type, callback, callback_type, sort_order) + values + (v_callback_id, new.event_type, new.object_type, new.callback, new.callback_type, v_sort_order); + exception when dup_val_on_index then + select callback_id into v_callback_id + from subsite_callbacks + where event_type = new.event_type + and object_type = new.object_type + and callback_type = new.callback_type + and callback = new.callback; + end; + return v_callback_id; + + END new; + + + procedure delete ( + callback_id IN subsite_callbacks.callback_id%TYPE + ) + is + begin + delete from subsite_callbacks where callback_id=subsite_callback.delete.callback_id; + end delete; + +end subsite_callback; +/ +show errors; + + Index: openacs-4/packages/acs-tcl/acs-tcl.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/acs-tcl.info,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-tcl/acs-tcl.info 27 Mar 2001 23:12:27 -0000 1.2 +++ openacs-4/packages/acs-tcl/acs-tcl.info 5 Apr 2001 18:23:38 -0000 1.3 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + ACS Core Team The Tcl library procedures that comprise the ACS Kernel. 2001-03-06 @@ -20,41 +21,48 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - + - - Index: openacs-4/packages/acs-tcl/tcl/10-database-procs-postgresql.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/Attic/10-database-procs-postgresql.tcl,v diff -u -r1.3 -r1.4 --- openacs-4/packages/acs-tcl/tcl/10-database-procs-postgresql.tcl 30 Mar 2001 05:44:47 -0000 1.3 +++ openacs-4/packages/acs-tcl/tcl/10-database-procs-postgresql.tcl 5 Apr 2001 18:23:38 -0000 1.4 @@ -7,10 +7,6 @@ @cvs-id $Id$ } -proc db_current_rdbms {} { - return [db_rdbms_create postgresql "7.1"] -} - proc_doc db_nextval { sequence } { Returns the next value for a sequence. This can utilize a pool of sequence values to save hits to the database. } { return [db_string nextval "select nextval('$sequence')"] } @@ -208,7 +204,8 @@ foreach line [split $error "\n"] { if { [string first NOTICE $line] == -1 } { append error_lines "$line\n" - set error_found [expr { $error_found || [string first ERROR $line] != -1 } ] + set error_found [expr { $error_found || [string first ERROR $line] != -1 || \ + [string first FATAL $line] != -1 } ] } } Index: openacs-4/packages/acs-tcl/tcl/10-database-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/Attic/10-database-procs.tcl,v diff -u -r1.5 -r1.6 --- openacs-4/packages/acs-tcl/tcl/10-database-procs.tcl 30 Mar 2001 05:44:47 -0000 1.5 +++ openacs-4/packages/acs-tcl/tcl/10-database-procs.tcl 5 Apr 2001 18:23:38 -0000 1.6 @@ -14,6 +14,31 @@ return [nsv_get ad_database_type .] } +proc_doc db_version { } { + Returns the RDBMS version (i.e. 8.1.6 is a recent Oracle version; 7.1 a + recent PostgreSQL version. +} { + return [nsv_get ad_database_version .] +} + +proc_doc db_current_rdbms { } { + Returns the current rdbms type and version. +} { + return [db_rdbms_create [db_type] [db_version]] +} + +proc_doc db_known_database_types { } { + Returns a list of three-element lists describing the database engines known + to OpenACS. Each sublist contains the internal database name (used in file + paths, etc), the driver name, and a "pretty name" to be used in selection + forms displayed to the user. + + The nsv containing the list is initialized by the bootstrap script and should + never be referenced directly by user code. +} { + return [nsv_get ad_known_database_types .] +} + proc_doc db_null { } { Returns an empty string, which Oracle thinks is null. This routine was invented to provide an RDBMS-specific null value but doesn't actually Index: openacs-4/packages/acs-tcl/tcl/apm-file-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/apm-file-procs-postgresql.xql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-tcl/tcl/apm-file-procs-postgresql.xql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,27 @@ + + + + + +select apm_package_version__add_file( + NULL, + :version_id, + :path, + :file_type, + :db_type +) + +postgresql7.1 + + + + +selec apm_package_version__remove_file ( + :path, + :version_id +) + +postgresql7.1 + + + Index: openacs-4/packages/acs-tcl/tcl/apm-file-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/apm-file-procs.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-tcl/tcl/apm-file-procs.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-tcl/tcl/apm-file-procs.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -60,10 +60,19 @@ Returns a list of valid file type keys. } { - return [util_memoize [list db_list unused "select file_type_key from apm_package_file_types"]] + return [util_memoize [list db_list file_type_keys "select file_type_key from apm_package_file_types"]] } +ad_proc -public apm_db_type_keys {} { + + Returns a list of valid database type keys. + +} { + return [util_memoize [list db_list db_type_keys "select db_type_key from apm_package_db_types"]] +} + + ad_proc -public apm_package_info_file_path { { -path "" @@ -156,7 +165,7 @@ { -file_id "" } - version_id path file_type + version_id path file_type db_type } { Adds one file into the specified version. @@ -172,7 +181,8 @@ file_id => :file_id, version_id => :version_id, path => :path, - file_type => :file_type + file_type => :file_type, + db_type => :db_type ); end; }] @@ -278,7 +288,8 @@ set components [split $relative_path "/"] set relative_path [join [lrange $components 2 [llength $components]] "/"] set type [apm_guess_file_type $package_key $relative_path] - apm_file_add $version_id $relative_path $type + set db_type [apm_guess_db_type $package_key $relative_path] + apm_file_add $version_id $relative_path $type $db_type } # Remove stale files. @@ -312,12 +323,14 @@ ad_proc -public apm_version_file_list { { - -type "" + -type "" -db_type "" } version_id } { Returns a list of paths to files of a given type (or all files, if - $type is not specified) in a version. + $type is not specified) which support a given database (if specified) in a version. @param type Optionally specifiy what type of files to check, for instance "tcl_procs" + @param db_type Optionally specifiy what type of database support to check, for instance + "postgresql" @param version_id The version to retrieve the file list from. } { @@ -326,10 +339,15 @@ } else { set type_sql "" } + if { ![empty_string_p $db_type] } { + set db_type_sql "and db_type = :db_type" + } else { + set db_type_sql "" + } return [db_list path_select " select path from apm_package_files where version_id = :version_id - $type_sql order by path + $type_sql $db_type_sql order by path "] } @@ -347,6 +365,9 @@ files.
  • Files with extension .sqlj are considered sqlj_code files.
  • Files with extension .info are considered package specification files. +
  • Files with extension .xql are considered query files. +
  • Files with extension .java are considered java code files. +
  • Files with extension .jar are considered java archive files.
  • Files with a path component named doc are considered documentation files.
  • Files with extension .pl or .sh or @@ -358,8 +379,10 @@ level of the package, are considered documentation files.
  • Files with a path component named www or admin-www are considered content-page files. -
  • Files ending in -procs.tcl or -init.tcl are considered +
  • Files ending in -procs(-)+()*.tcl) or -init.tcl are considered Tcl procedure or Tcl initialization files, respectively. +
  • File ending in .tcl are considered Tcl utility script files (normally + found only in the bootstrap installer). Rules are applied in this order (stopping with the first match). @@ -369,10 +392,17 @@ set extension [file extension $path] set type "" + # DRB: someone named a file "acs-mail-create-packages.sql" rather than + # the conventional "acs-mail-packages-create.sql", causing it to be + # recognized as a data_model_create file, causing it to be explicitly + # run by the installer (the author intended it to be included by + # acs-mail-create.sql only). I've tightened up the regexp below to + # avoid this problem, along with renaming the file... + if { [string equal $extension ".sql"] } { if { [lsearch -glob $components "*upgrade-*-*"] >= 0 } { set type "data_model_upgrade" - } elseif { [regexp -- "$package_key-(create|drop)" [file tail $path] "" kind] } { + } elseif { [regexp -- "$package_key-(create|drop)\.sql" [file tail $path] "" kind] } { set type "data_model_$kind" } else { set type "data_model" @@ -381,6 +411,12 @@ set type "sqlj_code" } elseif { [string equal $extension ".info"] } { set type "package_spec" + } elseif { [string equal $extension ".xql"] } { + set type "query_file" + } elseif { [string equal $extension ".java"] } { + set type "java_code" + } elseif { [string equal $extension ".jar"] } { + set type "java_archive" } elseif { [lsearch $components "doc"] >= 0 } { set type "documentation" } elseif { [string equal $extension ".pl"] || \ @@ -396,14 +432,69 @@ } elseif { [lsearch $components "www"] >= 0 || [lsearch $components "admin-www"] >= 0 } { set type "content_page" } else { - if { [string equal $extension ".tcl"] && \ - [regexp -- {-(procs|init)\.tcl$} [file tail $path] "" kind] } { - set type "tcl_$kind" + if { [string equal $extension ".tcl"] } { + if { [regexp -- {-(procs|init)(-[0-9a-zA-Z]*)?\.tcl$} [file tail $path] "" kind] } { + set type "tcl_$kind" + } else { + set type "tcl_util" + } } } return $type } + +ad_proc -private apm_guess_db_type { package_key path } { + + Guesses and returns the database type key corresponding to a particular path + (or an empty string if none is known). $path should be + relative to the package directory (e.g., www/index.tcl + for /packages/bboard/admin-www/index.tcl. + + We consider two cases: + + 1. Data model files. + + If the path contains a string matching "sql/" followed by a database type known + to this version of OpenACS, the file is assumed to be specific to that database type. + The empty string is returned for all other data model files. + + Example: "sql/postgresql/apm-create.sql" is assumed to be the PostgreSQL-specific + file used to create the APM datamodel. + + 2. Other files. + + If the file name contains a dash and database type, the file is assumed to be + specific to that database type. + + Example: "tcl/10-database-postgresql-proc.tcl" is asusmed to be the file that + defines the PostgreSQL-specific portions of the database API. + +} { + set components [split $path "/"] + + if { [string match "data_model*" [apm_guess_file_type $package_key $path]] } { + set sql_index [lsearch $components "sql"] + if { $sql_index >= 0 } { + set db_dir [lindex $components [expr $sql_index + 1]] + foreach known_database_type [db_known_database_types] { + if { [string match [lindex $known_database_type 0] $db_dir] } { + return $db_dir + } + } + } + return "" + } + + set file_name [file tail $path] + foreach known_database_type [db_known_database_types] { + if { [string match "*-[lindex $known_database_type 0]\.*" $file_name] } { + return [lindex $known_database_type 0] + } + } + return "" +} + ad_proc -private apm_ignore_file_p { path } { Return 1 if $path should, in general, be ignored for package operations. Index: openacs-4/packages/acs-tcl/tcl/apm-install-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/apm-install-procs-postgresql.xql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-tcl/tcl/apm-install-procs-postgresql.xql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,97 @@ + + + + +select apm_package_version__version_name_greater(service_version, :dependency_version) as version_p + from apm_package_dependencies d, apm_package_types a, apm_package_versions v + where d.dependency_type = 'provides' + and d.version_id = v.version_id + and d.service_uri = :dependency_uri + and v.installed_p = 't' + and a.package_key = v.package_key + +postgresql7.1 + + + + +SELECT apm_package_version__enable(:version_id); + +postgresql7.1 + + + + +SELECT apm__register_application ( + :package_key, + :pretty_name, + :pretty_plural, + :package_uri, + :singleton_p, + :spec_file_path, + :spec_file_mtime); + +postgresql7.1 + + + + +SELECT apm__register_service ( + :package_key, + :pretty_name, + :pretty_plural, + :package_uri, + :singleton_p, + :spec_file_path, + :spec_file_mtime); + +postgresql7.1 + + + + +select version_id +from apm_package_versions +where package_key = :package_key +and version_id = apm_package__highest_version(:package_key) + +postgresql7.1 + + + + +select apm_package_version__new( + :version_id, + :package_key, + :version_name, + :version_uri, + :summary, + :description_format, + :description, + :release_date, + :vendor, + :vendor_uri, + 't', + 't'); + +postgresql7.1 + + + + +select apm_package__new( + NULL, + :package_name, + :package_key, + 'apm_package', + now(), + NULL, + NULL, + acs__magic_object_id('default_context') +) + +postgresql7.1 + + + + Fisheye: Tag 1.3 refers to a dead (removed) revision in file `openacs-4/packages/acs-tcl/tcl/apm-install-procs.postgresql.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-tcl/tcl/apm-install-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/apm-install-procs.tcl,v diff -u -r1.4 -r1.5 --- openacs-4/packages/acs-tcl/tcl/apm-install-procs.tcl 3 Apr 2001 04:28:59 -0000 1.4 +++ openacs-4/packages/acs-tcl/tcl/apm-install-procs.tcl 5 Apr 2001 18:23:38 -0000 1.5 @@ -43,7 +43,11 @@ if { [apm_package_installed_p $package_key] } { # Load up the queries (OpenACS Query Dispatcher - ben) - apm_package_install_queries $package_key + # DRB: shouldn't be done here ... this routine just scans for uninstalled + # packages and shouldn't have any side effects of this sort. Bootstrap.tcl + # already loads queries for installed packages and the APM installer should + # be doing this when the user asks to install a package ... + # apm_package_install_queries $package_key if {$new_p} { continue @@ -734,7 +738,8 @@ set path [lindex $item 0] set file_type [lindex $item 1] - apm_file_add $version_id $path $file_type + set db_type [lindex $item 2] + apm_file_add $version_id $path $file_type $db_type } } @@ -747,9 +752,9 @@ } { set path "[acs_package_root_dir $package_key]" - # Traverse path for www/*.sql files + # Traverse path for www/*.xql files set files [glob -nocomplain ${path}/www/*.sql] - set files [concat $files [glob -nocomplain ${path}/tcl/*.sql]] + set files [concat $files [glob -nocomplain ${path}/tcl/*.xql]] ns_log Notice "APM/QD = loading up package query files for $package_key" @@ -802,7 +807,7 @@ } else { # Nothing there! We need to add a .info file. set path "$package_key.info" - apm_file_add $version_id $path package_spec + apm_file_add $version_id $path package_spec "" } ns_log Debug "APM: Writing APM .info file to the database." db_dml apm_spec_file_register { @@ -1111,8 +1116,16 @@ foreach file $file_list { set path [lindex $file 0] set file_type [lindex $file 1] - ns_log Debug "APM: Checking $path of type $file_type." - if {[lsearch -exact $types_to_retrieve $file_type] != -1} { + set file_db_type [lindex $file 2] + ns_log Debug "APM: Checking \"$path\" of type \"$file_type\" and db_type \"$file_db_type\"." + + # DRB: we return datamodel files which match the given database type or for which no db_type + # is defined. The latter case is a kludge to simplify support of legacy ACS Oracle-only + # modules which haven't had their datamodel files moved to sql/oracle. Eventually we should + # remove the kludge and insist that datamodel files live in the proper directory. + + if {[lsearch -exact $types_to_retrieve $file_type] != -1 && \ + ([empty_string_p $file_db_type] || ![string compare [db_type] $file_db_type])} { if { ![string compare $file_type "data_model_upgrade"] } { if {[apm_upgrade_for_version_p $path $upgrade_from_version_name \ $upgrade_to_version_name]} { Index: openacs-4/packages/acs-tcl/tcl/apm-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/apm-procs-postgresql.xql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-tcl/tcl/apm-procs-postgresql.xql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,62 @@ + + + + + +select apm__register_parameter ( + NULL, + :package_key, + :parameter_name, + :description, + :datatype, + :default_value, + :section_name, + :min_n_values, + :max_n_values); + +postgresql7.1 + + + + +select v.package_id, p.parameter_name, coalesce(p.default_value, v.attr_value) as attr_value +from apm_parameters p LEFT JOIN apm_parameter_values v using (parameter_id) +where p.package_key = :package_key + +postgresql7.1 + + + + +select apm_package_version__add_interface( + :interface_id, + :version_id, + :interface_uri, + :interface_version +) + +postgresql7.1 + + + + +select apm_package_version__remove_interface( + :interface_id +); + +postgresql7.1 + + + + +select apm_package_version__add_dependency( + :dependency_id, + :version_id, + :dependency_uri, + :dependency_version +) + +postgresql7.1 + + + Fisheye: Tag 1.4 refers to a dead (removed) revision in file `openacs-4/packages/acs-tcl/tcl/apm-procs.postgresql.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-tcl/tcl/apm-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/apm-procs.tcl,v diff -u -r1.2 -r1.3 --- openacs-4/packages/acs-tcl/tcl/apm-procs.tcl 27 Mar 2001 23:12:27 -0000 1.2 +++ openacs-4/packages/acs-tcl/tcl/apm-procs.tcl 5 Apr 2001 18:23:38 -0000 1.3 @@ -308,16 +308,21 @@ set base_len [string length $base] # For now we expect the SQL files to be in parallel with the Tcl files - set dirs [list ${base}www] + + # DRB: without the quotes ${base}www turns into the two element list + # {${base} www}, not what was wanted... + set dirs [list "${base}www"] set paths [list] - + + # DRB: For now just slurp all .sql files foreach dir $dirs { - set paths [concat $paths [glob -nocomplain "$dir/*.tcl"]] + set paths [concat $paths [glob -nocomplain "$dir/*.sql"]] } foreach path [lsort $paths] { set rel_path [string range $path $base_len end] - lappend files [list $package $rel_path] + # DRB: db_fullquery_internal_load_cache expects the full pathname... + lappend files "$base/$rel_path" } } @@ -342,6 +347,19 @@ " -default "Unknown" -bind [list type $type]]] } +ad_proc -private apm_pretty_name_for_db_type { db_type } { + + Returns the pretty name corresponding to a particular file type key + (memoizing to save a database hit here and there). + +} { + return [util_memoize [list db_string pretty_db_name_select " + select pretty_db_name + from apm_package_db_types + where db_type_key = :db_type + " -default "all" -bind [list db_type $db_type]]] +} + ad_proc -public apm_load_any_changed_libraries {} { In the running interpreter, reloads files marked for reload by Index: openacs-4/packages/acs-tcl/tcl/apm-xml-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/apm-xml-procs.tcl,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-tcl/tcl/apm-xml-procs.tcl 13 Mar 2001 22:59:26 -0000 1.1 +++ openacs-4/packages/acs-tcl/tcl/apm-xml-procs.tcl 5 Apr 2001 18:23:38 -0000 1.2 @@ -94,10 +94,17 @@ $singleton_p - - oracle-8.1.6 - -" + \n" + + db_foreach supported_databases { + select unique db_type + from apm_package_files + where db_type is not null + } { + append spec " $db_type\n" + } + append spec " \n" + db_foreach owner_info { select owner_uri, owner_name from apm_package_owners @@ -150,11 +157,14 @@ append spec "\n \n" ns_log Debug "APM: Writing Files." - db_foreach version_path "select path, file_type from apm_package_files where version_id = :version_id order by path" { + db_foreach version_path "select path, file_type, db_type from apm_package_files where version_id = :version_id order by path" { append spec " \n" } else { append spec " \n" @@ -339,12 +349,18 @@ foreach file_node [dom::element getElementsByTagName $node "file"] { set file_path [apm_required_attribute_value $file_node path] set type [dom::element getAttribute $file_node type] + set db_type [dom::element getAttribute $file_node db_type] # Validate the file type: it must be null (unknown type) or # some value in [apm_file_type_keys]. if { ![empty_string_p $type] && [lsearch -exact [apm_file_type_keys] $type] < 0 } { error "Invalid file type \"$type\"" } - lappend properties(files) [list $file_path $type] + # Validate the database type: it must be null (unknown type) or + # some value in [apm_db_type_keys]. + if { ![empty_string_p $db_type] && [lsearch -exact [apm_db_type_keys] $db_type] < 0 } { + error "Invalid database type \"$db_type\"" + } + lappend properties(files) [list $file_path $type $db_type] } } Index: openacs-4/packages/acs-templating/acs-templating.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/acs-templating.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/acs-templating/acs-templating.info 13 Mar 2001 22:59:27 -0000 1.1 +++ openacs-4/packages/acs-templating/acs-templating.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ t - - oracle-8.1.6 - + + oracle + postgresql + Christian Brechb Karl Goldstein Separate page layout and implementation. @@ -23,7 +24,7 @@ - + @@ -37,10 +38,10 @@ - - - - + + + + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-templating/sql/acs-templating-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-templating/sql/acs-templating-drop.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-templating/sql/demo-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/acs-templating/sql/demo-drop.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/acs-templating/sql/oracle/acs-templating-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/oracle/acs-templating-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/oracle/acs-templating-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,12 @@ +-- Data model to support content ACS Templating System + +-- Copyright (C) 1999-2000 ArsDigita Corporation +-- Author: Karl Goldstein (karlg@arsdigita.com) + +-- $Id: acs-templating-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ + +-- 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 + +@@ demo-create.sql Index: openacs-4/packages/acs-templating/sql/oracle/acs-templating-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/oracle/acs-templating-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/oracle/acs-templating-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,9 @@ +-- Uninstall file for the data model created by 'acs-templating-create.sql' +-- (This file created automatically by create-sql-uninst.pl.) +-- +-- brech (Mon Aug 28 11:04:55 2000) +-- +-- $Id: acs-templating-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +@@ demo-drop Index: openacs-4/packages/acs-templating/sql/oracle/demo-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/oracle/demo-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/oracle/demo-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,26 @@ +create sequence ad_template_sample_users_seq start with 5 increment by 1; + +create table ad_template_sample_users ( + user_id integer primary key, + first_name varchar2(20), + last_name varchar2(20), + address1 varchar2(40), + address2 varchar2(40), + city varchar2(40), + state varchar2(2) +); + + +insert into ad_template_sample_users values + (1, 'Fred', 'Jones', '101 Main St.', NULL, 'Orange', 'CA'); + +insert into ad_template_sample_users values + (2, 'Frieda', 'Mae', 'Lexington Hospital', '102 Central St.', + 'Orange', 'CA'); + +insert into ad_template_sample_users values + (3, 'Sally', 'Saxberg', 'Board of Supervisors', '1933 Fruitvale St.', + 'Woodstock', 'CA'); + +insert into ad_template_sample_users values + (4, 'Yoruba', 'Diaz', '12 Magic Ave.', NULL, 'Lariot', 'WY'); Index: openacs-4/packages/acs-templating/sql/oracle/demo-drop.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/oracle/demo-drop.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/oracle/demo-drop.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,10 @@ +-- Uninstall file for the data model created by 'demo-create.sql' +-- (This file created automatically by create-sql-uninst.pl.) +-- +-- brech (Mon Aug 28 11:06:33 2000) +-- +-- $Id: demo-drop.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +drop table ad_template_sample_users; +drop sequence ad_template_sample_users_seq; Index: openacs-4/packages/page/page.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/page/page.info,v diff -u -r1.1 -r1.2 --- openacs-4/packages/page/page.info 13 Mar 2001 22:59:27 -0000 1.1 +++ openacs-4/packages/page/page.info 5 Apr 2001 18:23:38 -0000 1.2 @@ -7,9 +7,10 @@ f - - oracle-8.1.6 - + + oracle + postgresql + Rafael Schloming Creates a page. ArsDigita Corporation @@ -18,7 +19,7 @@ - + Fisheye: Tag 1.2 refers to a dead (removed) revision in file `openacs-4/packages/page/sql/page-create.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/packages/page/sql/oracle/page-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/page/sql/oracle/page-create.sql,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/page/sql/oracle/page-create.sql 5 Apr 2001 18:23:38 -0000 1.1 @@ -0,0 +1,15 @@ +-- +-- packages/acs-page/sql/page-create.sql +-- +-- @author rhs@mit.edu +-- @creation-date 2000-09-29 +-- @cvs-id $Id: page-create.sql,v 1.1 2001/04/05 18:23:38 donb Exp $ +-- + +create table pages ( + package_id constraint pages_package_id_fk + references apm_packages (package_id) + constraint pages_package_id_pk + primary key, + content clob not null +);