Index: openacs-4/packages/acs-authentication/sql/oracle/batch-job-tables-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/sql/oracle/batch-job-tables-create.sql,v diff -u -N -r1.3 -r1.4 --- openacs-4/packages/acs-authentication/sql/oracle/batch-job-tables-create.sql 9 Sep 2003 11:38:26 -0000 1.3 +++ openacs-4/packages/acs-authentication/sql/oracle/batch-job-tables-create.sql 10 Sep 2003 09:09:46 -0000 1.4 @@ -18,7 +18,8 @@ not null, authority_id integer constraint auth_batch_jobs_auth_fk - references auth_authorities(authority_id), + references auth_authorities(authority_id) + on delete cascade, message varchar2(4000), -- if interactive, by which user creation_user integer @@ -34,6 +35,7 @@ ); create index auth_batch_jobs_user_idx on auth_batch_jobs(creation_user); +create index auth_batch_jobs_auth_idx on auth_batch_jobs(authority_id); create sequence auth_batch_job_entry_id_seq; @@ -50,10 +52,6 @@ operation varchar(100) constraint auth_batch_jobs_entries_op_ck check (operation in ('insert', 'update', 'delete')), - authority_id integer - constraint auth_batch_job_entries_auth_fk - references auth_authorities(authority_id) - on delete cascade, username varchar(100), user_id integer constraint auth_batch_job_entries_user_fk @@ -68,7 +66,6 @@ ); create index auth_batch_job_ent_job_idx on auth_batch_job_entries(job_id); -create index auth_batch_job_ent_auth_idx on auth_batch_job_entries(authority_id); create index auth_batch_job_ent_user_idx on auth_batch_job_entries(user_id); Index: openacs-4/packages/acs-authentication/sql/postgresql/batch-job-tables-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/sql/postgresql/batch-job-tables-create.sql,v diff -u -N -r1.3 -r1.4 --- openacs-4/packages/acs-authentication/sql/postgresql/batch-job-tables-create.sql 9 Sep 2003 11:38:26 -0000 1.3 +++ openacs-4/packages/acs-authentication/sql/postgresql/batch-job-tables-create.sql 10 Sep 2003 09:09:47 -0000 1.4 @@ -15,7 +15,8 @@ not null, authority_id integer constraint auth_batch_jobs_auth_fk - references auth_authorities(authority_id), + references auth_authorities(authority_id) + on delete cascade, message text, -- if interactive, by which user creation_user integer @@ -31,6 +32,7 @@ ); create index auth_batch_jobs_user_idx on auth_batch_jobs(creation_user); +create index auth_batch_jobs_auth_idx on auth_batch_jobs(authority_id); create sequence auth_batch_job_entry_id_seq; @@ -47,10 +49,6 @@ operation varchar(100) constraint auth_batch_jobs_entries_op_ck check (operation in ('insert', 'update', 'delete')), - authority_id integer - constraint auth_batch_job_entries_auth_fk - references auth_authorities(authority_id) - on delete cascade, username varchar(100), user_id integer constraint auth_batch_job_entries_user_fk @@ -61,7 +59,6 @@ ); create index auth_batch_job_ent_job_idx on auth_batch_job_entries(job_id); -create index auth_batch_job_ent_auth_idx on auth_batch_job_entries(authority_id); create index auth_batch_job_ent_user_idx on auth_batch_job_entries(user_id); Index: openacs-4/packages/acs-authentication/tcl/apm-callback-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/tcl/apm-callback-procs.tcl,v diff -u -N -r1.5 -r1.6 --- openacs-4/packages/acs-authentication/tcl/apm-callback-procs.tcl 9 Sep 2003 12:31:59 -0000 1.5 +++ openacs-4/packages/acs-authentication/tcl/apm-callback-procs.tcl 10 Sep 2003 09:09:47 -0000 1.6 @@ -29,13 +29,19 @@ # Register HTTP method for GetDocument auth::sync::get_doc::http::register_impl + + # Register IMS Enterprise 1.1 ProcessDocument implementation + auth::sync::process_doc::ims::register_impl } } ad_proc -private auth::package_uninstall {} {} { db_transaction { + # Unregister IMS Enterprise 1.1 ProcessDocument implementation + auth::sync::process_doc::ims::unregister_impl + # Unregister HTTP method for GetDocument auth::sync::get_doc::http::unregister_impl Index: openacs-4/packages/acs-authentication/tcl/authentication-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/tcl/authentication-procs.tcl,v diff -u -N -r1.22 -r1.23 --- openacs-4/packages/acs-authentication/tcl/authentication-procs.tcl 9 Sep 2003 11:15:03 -0000 1.22 +++ openacs-4/packages/acs-authentication/tcl/authentication-procs.tcl 10 Sep 2003 09:09:47 -0000 1.23 @@ -892,7 +892,7 @@ if { [empty_string_p $user_id] } { set result(delete_status) "delete_error" - set result(delete_messages) "No user found with this username" + set result(delete_message) "No user found with this username" return [array get result] } @@ -924,7 +924,7 @@ foreach elm { authority_id username first_names last_name email } { if { ![exists_and_not_null user($elm)] } { - set element_messages(first_names) "Required" + set element_messages($elm) "Required" } } @@ -934,7 +934,7 @@ -username $user(username)] if { [empty_string_p $user(user_id)] } { - set element_messages(username) "No user with username '$username' found for authority [auth::authority::get_element -authority_id $authority_id -element pretty_name]" + set element_messages(username) "No user with username '$user(username)' found for authority [auth::authority::get_element -authority_id $user(authority_id) -element pretty_name]" } } Index: openacs-4/packages/acs-authentication/tcl/authority-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/tcl/authority-procs.tcl,v diff -u -N -r1.10 -r1.11 --- openacs-4/packages/acs-authentication/tcl/authority-procs.tcl 10 Sep 2003 08:58:57 -0000 1.10 +++ openacs-4/packages/acs-authentication/tcl/authority-procs.tcl 10 Sep 2003 09:09:47 -0000 1.11 @@ -297,15 +297,17 @@ } { global errorInfo ns_log Error "Error getting sync document:\n$errorInfo" + set doc_result(doc_status) failed_to_connect + set doc_result(doc_message) $errmsg } - auth::sync::end_get_document \ + auth::sync::job::end_get_document \ -job_id $job_id \ -doc_status $doc_result(doc_status) \ -doc_message $doc_result(doc_message) \ -document $doc_result(document) - if { [string equal $doc_status "ok"] && ![empty_string_p $doc_result(document)] } { + if { [string equal $doc_result(doc_status) "ok"] && ![empty_string_p $doc_result(document)] } { with_catch errmsg { auth::sync::ProcessDocument \ -authority_id $authority_id \ @@ -316,6 +318,10 @@ ns_log Error "Error processing sync document:\n$errorInfo" set message "Error processing sync document: $errmsg" } + } else { + if { [empty_string_p $message] } { + set message $doc_result(doc_message) + } } if { $snapshot_p } { Index: openacs-4/packages/acs-authentication/tcl/sync-procs-oracle.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/tcl/sync-procs-oracle.xql,v diff -u -N -r1.3 -r1.4 --- openacs-4/packages/acs-authentication/tcl/sync-procs-oracle.xql 9 Sep 2003 12:31:59 -0000 1.3 +++ openacs-4/packages/acs-authentication/tcl/sync-procs-oracle.xql 10 Sep 2003 09:09:47 -0000 1.4 @@ -55,9 +55,9 @@ insert into auth_batch_job_entries - (entry_id, job_id, operation, authority_id, username, user_id, success_p, message, element_messages) + (entry_id, job_id, operation, username, user_id, success_p, message, element_messages) values - (:entry_id, :job_id, :operation, :authority_id, :username, :user_id, :success_p_db, :message, empty_clob()) + (:entry_id, :job_id, :operation, :username, :user_id, :success_p_db, :message, empty_clob()) returning element_messages into :1 Index: openacs-4/packages/acs-authentication/tcl/sync-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/tcl/sync-procs-postgresql.xql,v diff -u -N -r1.3 -r1.4 --- openacs-4/packages/acs-authentication/tcl/sync-procs-postgresql.xql 9 Sep 2003 12:31:59 -0000 1.3 +++ openacs-4/packages/acs-authentication/tcl/sync-procs-postgresql.xql 10 Sep 2003 09:09:47 -0000 1.4 @@ -55,9 +55,9 @@ insert into auth_batch_job_entries - (entry_id, job_id, operation, authority_id, username, user_id, success_p, message, element_messages) + (entry_id, job_id, operation, username, user_id, success_p, message, element_messages) values - (:entry_id, :job_id, :operation, :authority_id, :username, :user_id, :success_p_db, :message, :element_messages) + (:entry_id, :job_id, :operation, :username, :user_id, :success_p_db, :message, :element_messages) Index: openacs-4/packages/acs-authentication/tcl/sync-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/tcl/sync-procs.tcl,v diff -u -N -r1.5 -r1.6 --- openacs-4/packages/acs-authentication/tcl/sync-procs.tcl 9 Sep 2003 12:31:59 -0000 1.5 +++ openacs-4/packages/acs-authentication/tcl/sync-procs.tcl 10 Sep 2003 09:09:47 -0000 1.6 @@ -11,6 +11,8 @@ namespace eval auth::sync::job {} namespace eval auth::sync::get_doc {} namespace eval auth::sync::get_doc::http {} +namespace eval auth::sync::process_doc {} +namespace eval auth::sync::process_doc::ims {} ##### @@ -39,6 +41,73 @@ set row(log_url) [export_vars -base "[ad_url]/acs-admin/package/acs-authentication/sync-log" { job_id }] } +ad_proc -public auth::sync::job::get_entries { + {-job_id:required} +} { + Get a list of entry_ids of the job log entries, ordered by entry_time. + + @param job_id The ID of the batch job you're ending. + + @author Lars Pind (lars@collaboraid.biz) +} { + return [db_list select_entries { select entry_id from auth_batch_job_entries where job_id = :job_id order by entry_time }] +} + +ad_proc -public auth::sync::job::get_authority_id_from_job_id { + {-job_id:required} +} { + Get the authority_id from a job_id. Cached. + + @param job_id The ID of the batch job you're ending. + + @author Lars Pind (lars@collaboraid.biz) +} { + return [util_memoize [list auth::sync::job::get_authority_id_from_job_id_not_cached $job_id]] +} + +ad_proc -private auth::sync::job::get_authority_id_from_job_id_flush { + {-job_id ""} +} { + Flush cache + + @param job_id The ID of the batch job you're ending. + + @author Lars Pind (lars@collaboraid.biz) +} { + if { ![empty_string_p $job_id] } { + util_memoize_flush [list auth::sync::job::get_authority_id_from_job_id_not_cached $job_id] + } else { + util_memoize_flush_regexp [list auth::sync::job::get_authority_id_from_job_id_not_cached .*] + } +} + +ad_proc -private auth::sync::job::get_authority_id_from_job_id_seed { + {-job_id:required} + {-authority_id:required} +} { + Flush cache + + @param job_id The ID of the batch job you're ending. + + @author Lars Pind (lars@collaboraid.biz) +} { + util_memoize_seed [list auth::sync::job::get_authority_id_from_job_id_not_cached $job_id] $authority_id +} + +ad_proc -private auth::sync::job::get_authority_id_from_job_id_not_cached { + job_id +} { + Get the authority_id from a job_id. Not cached. + + @param job_id The ID of the batch job you're ending. + + @author Lars Pind (lars@collaboraid.biz) + + @see auth::sync::job::get_authority_id_from_job_id +} { + return [db_string select_auth_id { select authority_id from auth_batch_jobs where job_id = :job_id }] +} + ad_proc -public auth::sync::job::start { {-job_id ""} {-authority_id:required} @@ -75,6 +144,9 @@ } } + # See the cache, we're going to need it shortly + auth::sync::job::get_authority_id_from_job_id_seed -job_id $job_id -authority_id $authority_id + return $job_id } @@ -156,7 +228,6 @@ ad_proc -public auth::sync::job::create_entry { {-job_id:required} {-operation:required} - {-authority_id:required} {-username:required} {-user_id ""} {-success:boolean} @@ -169,8 +240,6 @@ @param operation One of 'insert', 'update', or 'delete'. - @param authority_id The authority this is about - @param username The username of the user being inserted/updated/deleted. @param user_id The user_id of the local user account, if known. @@ -199,26 +268,27 @@ upvar 1 $array row db_1row select_entry { - select entry_id, - job_id, - entry_time, - operation, - authority_id, - username, - user_id, - success_p, - message, - element_messages - from auth_batch_job_entries - where entry_id = :entry_id + select e.entry_id, + e.job_id, + e.entry_time, + e.operation, + j.authority_id, + e.username, + e.user_id, + e.success_p, + e.message, + e.element_messages + from auth_batch_job_entries e, + auth_batch_jobs j + where e.entry_id = :entry_id + and j.job_id = e.job_id } -column_array row } ad_proc -public auth::sync::job::action { {-job_id:required} {-operation:required} - {-authority_id:required} {-username:required} {-first_names ""} {-last_name ""} @@ -232,15 +302,15 @@ @param operation 'insert', 'update', 'delete', or 'snapshot'. - @param authority_id The authority involved - @param username The username which this action refers to. @return entry_id of newly created entry } { set entry_id {} set user_id {} + set authority_id [get_authority_id_from_job_id -job_id $job_id] + db_transaction { # We deal with insert/update in a snaphsot sync here if { [string equal $operation "snapshot"] } { @@ -328,7 +398,6 @@ set entry_id [auth::sync::job::create_entry \ -job_id $job_id \ -operation $operation \ - -authority_id $authority_id \ -username $username \ -user_id $user_id \ -success=$success_p \ @@ -340,11 +409,12 @@ } ad_proc -public auth::sync::job::snapshot_delete_remaining { - -job_id:required - -authority_id:required + {-job_id:required} } { Deletes the users that weren't included in the snapshot. } { + set authority_id [get_authority_id_from_job_id -job_id $job_id] + set usernames [db_list select_user_ids { select username from cc_users @@ -357,7 +427,6 @@ auth::sync::job::action \ -job_id $job_id \ -operation "delete" \ - -authority_id $authority_id \ -username $username } } @@ -418,6 +487,7 @@ return [acs_sc::invoke \ -error \ + -contract "GetDocument" \ -impl_id $impl_id \ -operation GetDocument \ -call_args [list $parameters]] @@ -511,3 +581,104 @@ } + + +##### +# +# auth::sync::process_doc::ims namespace +# +##### + +ad_proc -private auth::sync::process_doc::ims::register_impl {} { + Register this implementation +} { + set spec { + contract_name "auth_sync_process" + owner "acs-authentication" + name "IMS Enterprise 1.1" + aliases { + ProcessDocument auth::sync::process_doc::ims::ProcessDocument + GetParameters auth::sync::proecss_doc::ims::GetParameters + } + } + + return [acs_sc::impl::new_from_spec -spec $spec] + +} + +ad_proc -private auth::sync::process_doc::ims::unregister_impl {} { + Unregister this implementation +} { + acs_sc::impl::delete -contract_name "auth_sync_process" -impl_name "IMS Enterprise 1.1" +} + +ad_proc -private auth::sync::process_doc::ims::GetParameters {} { + Parameters for IMS Enterprise 1.1 auth_sync_process implementation. +} { + return {} +} + +ad_proc -private auth::sync::process_doc::ims::ProcessDocument { + job_id + document + parameters +} { + Process IMS Enterprise 1.1 document. +} { + set tree [xml_parse -persist $document] + + set root_node [xml_doc_get_first_node $tree] + + if { ![string equal [xml_node_get_name $root_node] "enterprise"] } { + error "Root node was not " + } + + # Loop over records + foreach person_node [xml_node_get_children_by_name $root_node "person"] { + switch [xml_node_get_attribute $person_node "recstatus"] { + 1 { + set operation "insert" + } + 2 { + set operation "update" + } + 3 { + set operation "delete" + } + default { + set operation "snapshot" + } + } + + # Initialize variables for this record + foreach elm { username first_names last_name email url } { + set $elm {} + } + + set username [xml_get_child_node_content_by_path $person_node { { userid } { sourcedid id } }] + set email [xml_get_child_node_content_by_path $person_node { { email } }] + set url [xml_get_child_node_content_by_path $person_node { { url } }] + + # We need a little more logic to deal with first_names/last_name, since they may not be split up in the XML + set first_names [xml_get_child_node_content_by_path $person_node { { name given } }] + set last_name [xml_get_child_node_content_by_path $person_node { { name family } }] + + if { [empty_string_p $first_names] || [empty_string_p $last_name] } { + set formatted_name [xml_get_child_node_content_by_path $person_node { { name fn } }] + if { ![empty_string_p $formatted_name] || [string first " " $formatted_name] > -1 } { + # Split, so everything up to the last space goes to first_names, the rest to last_name + regexp {^(.+) ([^ ]+)$} $formatted_name match first_names last_name + } + } + + auth::sync::job::action \ + -job_id $job_id \ + -operation $operation \ + -username $username \ + -first_names $first_names \ + -last_name $last_name \ + -email $email \ + -url $url + } +} + Index: openacs-4/packages/acs-authentication/tcl/test/sync-test-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-authentication/tcl/test/sync-test-procs.tcl,v diff -u -N -r1.5 -r1.6 --- openacs-4/packages/acs-authentication/tcl/test/sync-test-procs.tcl 9 Sep 2003 12:31:59 -0000 1.5 +++ openacs-4/packages/acs-authentication/tcl/test/sync-test-procs.tcl 10 Sep 2003 09:09:47 -0000 1.6 @@ -34,7 +34,6 @@ auth::sync::job::create_entry \ -job_id $job_id \ -operation "insert" \ - -authority_id [auth::authority::local] \ -username "foobar" \ -user_id [ad_conn user_id] \ -success @@ -43,7 +42,6 @@ auth::sync::job::create_entry \ -job_id $job_id \ -operation "insert" \ - -authority_id [auth::authority::local] \ -username "foobar" \ -user_id [ad_conn user_id] \ -message "A problem" \ @@ -111,7 +109,6 @@ set entry_id [auth::sync::job::action \ -job_id $job_id \ -operation "insert" \ - -authority_id [auth::authority::local] \ -username $username1 \ -first_names $first_names1 \ -last_name $last_name1 \ @@ -152,7 +149,6 @@ set entry_id [auth::sync::job::action \ -job_id $job_id \ -operation "insert" \ - -authority_id [auth::authority::local] \ -username $username1 \ -first_names [ad_generate_random_string] \ -last_name [ad_generate_random_string] \ @@ -190,7 +186,6 @@ set entry_id [auth::sync::job::action \ -job_id $job_id \ -operation "update" \ - -authority_id [auth::authority::local] \ -username $username1 \ -first_names $first_names2 \ -last_name $last_name2 \ @@ -232,7 +227,6 @@ set entry_id [auth::sync::job::action \ -job_id $job_id \ -operation "insert" \ - -authority_id [auth::authority::local] \ -username $username2 \ -first_names {} \ -last_name {Foobar} \ @@ -264,7 +258,6 @@ set entry_id [auth::sync::job::action \ -job_id $job_id \ -operation "delete" \ - -authority_id [auth::authority::local] \ -username $username1] array unset entry @@ -332,7 +325,6 @@ set entry_id [auth::sync::job::action \ -job_id $job_id \ -operation "snapshot" \ - -authority_id [auth::authority::local] \ -username $username1 \ -first_names $first_names1 \ -last_name $last_name1 \ @@ -378,7 +370,6 @@ set entry_id [auth::sync::job::action \ -job_id $job_id \ -operation "snapshot" \ - -authority_id [auth::authority::local] \ -username $username1 \ -first_names $first_names2 \ -last_name $last_name2 \ @@ -427,8 +418,7 @@ }] auth::sync::job::snapshot_delete_remaining \ - -job_id $job_id \ - -authority_id [auth::authority::local] + -job_id $job_id ##### # @@ -464,6 +454,192 @@ auth::sync::job::get -job_id $job_id -array job aa_log "job.message = '$job(message)'" + aa_true "job.message not empty when called for local authority" [exists_and_not_null job(message)] + } +} + +aa_register_case job_batch_ims_dummy { + Test a batch job for the local authority +} { + aa_stub acs_sc::invoke { + acs_sc::invoke__arg_parser + + if { [string equal $contract GetDocument] && [string equal $operation GetDocument] } { + array set result { + doc_status ok + doc_message {} + document {} + } + + # Example document grabbed pulled from + # http://www.imsglobal.org/enterprise/entv1p1/imsent_bestv1p1.html#1404584 + set result(document) { + + + Dunelm Services Limited + Telecommunications LMS + DATABASE UPDATE + 2001-08-08 + + + Add a new Person record. + + Dunelm Services Limited + CK1 + + + Clark Kent + Kent, C + Superman + + + 2 + + + The Daily Planet + Metropolis + USA + + + + Update a previously created record. + + Dunelm Services Limited + CS1 + + + Colin Smythe + Smythe, C + Colin + + Smythe + Colin + Manfred + Wingarde + Dr. + C.Eng + C.M.W. + + + + 2 + 1958-02-18 + None. + + colin@dunelm.com + http://www.dunelm.com + 4477932335019 + + Dunelm Services Limited + 34 Acorn Drive + Stannington + Sheffield + S.Yorks + S7 6WA + UK + + + http://www.dunelm.com/staff/colin2.gif + + + dunelm:colinsmythe:1 + + + Delete this record. + + Dunelm Services Limited + LL1 + + + Lois Lane + Lane, L + + + +} + + return [array get result] + } else { + acs_sc::invoke_unstubbed \ + -contract $contract \ + -operation $operation \ + -impl $impl \ + -impl_id $impl_id \ + -call_args $call_args \ + -error=$error_p } + } + + aa_run_with_teardown \ + -rollback \ + -test_code { + + # Create a new dummy authority with the dummy IMS get-document driver and the IMS Enterprise 1.1 process driver. + array set new_auth { + short_name dummy-test + pretty_name dummy-test + enabled_p t + sort_order 999 + auth_impl_id {} + pwd_impl_id {} + forgotten_pwd_url {} + change_pwd_url {} + register_impl_id {} + register_url {} + help_contact_text {} + snapshot_p f + batch_sync_enabled_p f + } + set new_auth(get_doc_impl_id) 1 + set new_auth(process_doc_impl_id) [acs_sc::impl::get_id -owner "acs-authentication" -name "IMS Enterprise 1.1"] + + set new_auth(get_doc_impl_id) [acs_sc::impl::get_id -owner "acs-authentication" -name "HTTPGet"] + + set authority_id [auth::authority::create \ + -array new_auth] + + set job_id [auth::authority::batch_sync -authority_id $authority_id] + + auth::sync::job::get -job_id $job_id -array job + + aa_equals "Number of actions" $job(num_actions) 3 + + aa_equals "Number of problems" $job(num_problems) 3 + + foreach entry_id [auth::sync::job::get_entries -job_id $job_id] { + array unset entry + auth::sync::job::get_entry \ + -entry_id $entry_id \ + -array entry + + aa_false "Success_p is false" [template::util::is_true $entry(success_p)] + + array unset elm_msgs + array set elm_msgs $entry(element_messages) + + aa_log "entry.operation = '$entry(operation)'" + aa_log "entry.username = '$entry(username)'" + aa_log "entry.message = '$entry(message)'" + aa_log "array names elm_msgs = '[array names elm_msgs]'" + + switch $entry(operation) { + insert { + aa_true "email has a problem (email missing)" [util_sets_equal_p { email } [array names elm_msgs]] + } + update { + aa_true "username has a problem (don't have this user)" [util_sets_equal_p { username } [array names elm_msgs]] + } + delete { + aa_false "Message is not empty" [empty_string_p $entry(message)] + } + } + } + + aa_log "job.message = '$job(message)'" + aa_log "job.document = '$job(document)'" + + } } + +