From cab1e088248377bb60722430f38ff9ad203635e0 Mon Sep 17 00:00:00 2001 From: miker Date: Mon, 11 Oct 2010 17:25:19 +0000 Subject: [PATCH] Backporting batch update functionality for 1.6.2, along with some deps git-svn-id: svn://svn.open-ils.org/ILS/branches/rel_1_6@18265 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/examples/opensrf.xml.example | 9 + .../OpenILS/Application/Actor/Container.pm | 98 ++++ Open-ILS/src/perlmods/OpenILS/Application/Cat.pm | 150 +++++ .../perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm | 613 +++++++++++++++++++++ Open-ILS/src/sql/Pg/950.data.seed-values.sql | 1 + .../0434.data.merge_template_container_type.sql | 9 + Open-ILS/web/css/theme/default.css | 1 + Open-ILS/web/js/dojo/MARC/Batch.js | 82 +++ Open-ILS/web/js/dojo/MARC/Field.js | 130 +++++ Open-ILS/web/js/dojo/MARC/Record.js | 299 ++++++++++ Open-ILS/web/js/dojo/fieldmapper/AutoIDL.js | 10 +- Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js | 168 +++--- Open-ILS/web/js/dojo/fieldmapper/IDL.js | 101 ++-- Open-ILS/web/js/dojo/fieldmapper/dojoData.js | 31 +- Open-ILS/web/js/dojo/fieldmapper/hash.js | 2 +- Open-ILS/web/js/dojo/openils/User.js | 12 +- Open-ILS/web/js/ui/base.js | 12 +- Open-ILS/web/templates/login.tt2 | 1 + 18 files changed, 1600 insertions(+), 129 deletions(-) create mode 100644 Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm create mode 100644 Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql create mode 100644 Open-ILS/web/js/dojo/MARC/Batch.js create mode 100644 Open-ILS/web/js/dojo/MARC/Field.js create mode 100644 Open-ILS/web/js/dojo/MARC/Record.js diff --git a/Open-ILS/examples/opensrf.xml.example b/Open-ILS/examples/opensrf.xml.example index 56be38c1ec..5c0c74bdae 100644 --- a/Open-ILS/examples/opensrf.xml.example +++ b/Open-ILS/examples/opensrf.xml.example @@ -283,6 +283,15 @@ vim:et:ts=4:sw=4: 86400 + + + + localhost:11211 + + 1800 + + 102400 + diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm b/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm index 67f91a04b1..08083c0d1e 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm @@ -7,6 +7,10 @@ use Data::Dumper; use OpenSRF::EX qw(:try); use OpenILS::Utils::Fieldmapper; use OpenILS::Utils::CStoreEditor qw/:funcs/; +use OpenSRF::Utils::SettingsClient; +use OpenSRF::Utils::Cache; +use Digest::MD5 qw(md5_hex); +use OpenSRF::Utils::JSON; my $apputils = "OpenILS::Application::AppUtils"; my $U = $apputils; @@ -506,6 +510,100 @@ sub container_update { +__PACKAGE__->register_method( + method => "anon_cache", + api_name => "open-ils.actor.anon_cache.set_value", + signature => { + desc => q/ + Sets a value in the anon web cache. If the session key is + undefined, one will be automatically generated. + /, + params => [ + {desc => 'Session key', type => 'string'}, + { + desc => q/Field name. The name of the field in this cache session whose value to set/, + type => 'string' + }, + { + desc => q/The cached value. This can be any type of object (hash, array, string, etc.)/, + type => 'any' + }, + ], + return => { + desc => 'session key on success, undef on error', + type => 'string' + } + } +); + +__PACKAGE__->register_method( + method => "anon_cache", + api_name => "open-ils.actor.anon_cache.get_value", + signature => { + desc => q/ + Returns the cached data at the specified field within the specified cache session. + /, + params => [ + {desc => 'Session key', type => 'string'}, + { + desc => q/Field name. The name of the field in this cache session whose value to set/, + type => 'string' + }, + ], + return => { + desc => 'cached value on success, undef on error', + type => 'any' + } + } +); + +__PACKAGE__->register_method( + method => "anon_cache", + api_name => "open-ils.actor.anon_cache.delete_session", + signature => { + desc => q/ + Deletes a cache session. + /, + params => [ + {desc => 'Session key', type => 'string'}, + ], + return => { + desc => 'Session key', + type => 'string' + } + } +); + +sub anon_cache { + my($self, $conn, $ses_key, $field_key, $value) = @_; + + my $sc = OpenSRF::Utils::SettingsClient->new; + my $cache = OpenSRF::Utils::Cache->new('anon'); + my $cache_timeout = $sc->config_value(cache => anon => 'max_cache_time') || 1800; # 30 minutes + my $cache_size = $sc->config_value(cache => anon => 'max_cache_size') || 102400; # 100k + + if($self->api_name =~ /delete_session/) { + + return $cache->delete_cache($ses_key); + + } elsif( $self->api_name =~ /set_value/ ) { + + $ses_key = md5_hex(time . rand($$)) unless $ses_key; + my $blob = $cache->get_cache($ses_key) || {}; + $blob->{$field_key} = $value; + return undef if + length(OpenSRF::Utils::JSON->perl2JSON($blob)) > $cache_size; # bytes, characters, whatever ;) + $cache->put_cache($ses_key, $blob, $cache_timeout); + return $ses_key; + + } else { + + my $blob = $cache->get_cache($ses_key) or return undef; + return $blob->{$field_key}; + } +} + + 1; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm b/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm index f18e63139d..8f4ee12e97 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm @@ -175,6 +175,156 @@ sub biblio_record_replace_marc { } __PACKAGE__->register_method( + method => "template_overlay_biblio_record_entry", + api_name => "open-ils.cat.biblio.record_entry.template_overlay", + stream => 1, + signature => q# + Overlays biblio.record_entry MARC values + @param auth The authtoken + @param records The record ids to be updated by the template + @param template The overlay template + @return Stream of hashes record id in the key "record" and t or f for the success of the overlay operation in key "success" + # +); + +sub template_overlay_biblio_record_entry { + my($self, $conn, $auth, $records, $template) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + + $records = [$records] if (!ref($records)); + + for my $rid ( @$records ) { + my $rec = $e->retrieve_biblio_record_entry($rid); + next unless $rec; + + unless ($e->allowed('UPDATE_RECORD', $rec->owner, $rec)) { + $conn->respond({ record => $rid, success => 'f' }); + next; + } + + my $success = $e->json_query( + { from => [ 'vandelay.template_overlay_bib_record', $template, $rid ] } + )->[0]->{'vandelay.template_overlay_bib_record'}; + + $conn->respond({ record => $rid, success => $success }); + } + + $e->commit; + return undef; +} + +__PACKAGE__->register_method( + method => "template_overlay_container", + api_name => "open-ils.cat.container.template_overlay", + stream => 1, + signature => q# + Overlays biblio.record_entry MARC values + @param auth The authtoken + @param container The container, um, containing the records to be updated by the template + @param template The overlay template, or nothing and the method will look for a negative bib id in the container + @return Stream of hashes record id in the key "record" and t or f for the success of the overlay operation in key "success" + # +); + +__PACKAGE__->register_method( + method => "template_overlay_container", + api_name => "open-ils.cat.container.template_overlay.background", + stream => 1, + signature => q# + Overlays biblio.record_entry MARC values + @param auth The authtoken + @param container The container, um, containing the records to be updated by the template + @param template The overlay template, or nothing and the method will look for a negative bib id in the container + @return Cache key to check for status of the container overlay + # +); + +sub template_overlay_container { + my($self, $conn, $auth, $container, $template) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->die_event unless $e->checkauth; + + my $actor = OpenSRF::AppSession->create('open-ils.actor') if ($self->api_name =~ /background$/); + + my $items = $e->search_container_biblio_record_entry_bucket_item({ bucket => $container }); + + my $titem; + if (!$template) { + ($titem) = grep { $_->target_biblio_record_entry < 0 } @$items; + if (!$titem) { + $e->rollback; + return undef; + } + $items = [grep { $_->target_biblio_record_entry > 0 } @$items]; + + $template = $e->retrieve_biblio_record_entry( $titem->target_biblio_record_entry )->marc; + } + + my $responses = []; + my $some_failed = 0; + + $self->respond_complete( + $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses)->gather(1) + ) if ($actor); + + for my $item ( @$items ) { + my $rec = $e->retrieve_biblio_record_entry($item->target_biblio_record_entry); + next unless $rec; + + my $success = 'f'; + if ($e->allowed('UPDATE_RECORD', $rec->owner, $rec)) { + $success = $e->json_query( + { from => [ 'vandelay.template_overlay_bib_record', $template, $rec->id ] } + )->[0]->{'vandelay.template_overlay_bib_record'}; + } + + $some_failed++ if ($success eq 'f'); + + if ($actor) { + push @$responses, { record => $rec->id, success => $success }; + $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses); + } else { + $conn->respond({ record => $rec->id, success => $success }); + } + + if ($success eq 't') { + unless ($e->delete_container_biblio_record_entry_bucket_item($item)) { + $e->rollback; + if ($actor) { + push @$responses, { complete => 1, success => 'f' }; + $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses); + return undef; + } else { + return { complete => 1, success => 'f' }; + } + } + } + } + + if ($titem && !$some_failed) { + return $e->die_event unless ($e->delete_container_biblio_record_entry_bucket_item($titem)); + } + + if ($e->commit) { + if ($actor) { + push @$responses, { complete => 1, success => 't' }; + $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses); + } else { + return { complete => 1, success => 't' }; + } + } else { + if ($actor) { + push @$responses, { complete => 1, success => 'f' }; + $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses); + } else { + return { complete => 1, success => 'f' }; + } + } + return undef; +} + +__PACKAGE__->register_method( method => "update_biblio_record_entry", api_name => "open-ils.cat.biblio.record_entry.update", signature => q/ diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm b/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm new file mode 100644 index 0000000000..b51d69ab32 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm @@ -0,0 +1,613 @@ +package OpenILS::WWW::TemplateBatchBibUpdate; +use strict; +use warnings; +use bytes; + +use Apache2::Log; +use Apache2::Const -compile => qw(OK REDIRECT DECLINED NOT_FOUND :log); +use APR::Const -compile => qw(:error SUCCESS); +use APR::Table; + +use Apache2::RequestRec (); +use Apache2::RequestIO (); +use Apache2::RequestUtil; +use CGI; +use Data::Dumper; +use Text::CSV; + +use OpenSRF::EX qw(:try); +use OpenSRF::Utils qw/:datetime/; +use OpenSRF::Utils::Cache; +use OpenSRF::System; +use OpenSRF::AppSession; +use XML::LibXML; +use XML::LibXSLT; + +use Encode; +use Unicode::Normalize; +use OpenILS::Utils::Fieldmapper; +use OpenSRF::Utils::Logger qw/$logger/; + +use MARC::Record; +use MARC::File::XML; + +use UNIVERSAL::require; + +our @formats = qw/USMARC UNIMARC XML BRE/; + +# set the bootstrap config and template include directory when +# this module is loaded +my $bootstrap; + +sub import { + my $self = shift; + $bootstrap = shift; +} + + +sub child_init { + OpenSRF::System->bootstrap_client( config_file => $bootstrap ); + Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL")); +} + +sub handler { + my $r = shift; + my $cgi = new CGI; + + my $authid = $cgi->cookie('ses') || $cgi->param('ses'); + my $usr = verify_login($authid); + return show_template($r) unless ($usr); + + + my $template = $cgi->param('template'); + return show_template($r) unless ($template); + + # find some IDs ... + my @records; + + @records = map { $_ ? ($_) : () } $cgi->param('recid'); + + if (!@records) { # try for a file + my $file = $cgi->param('idfile'); + if ($file) { + my $col = $cgi->param('idcolumn') || 0; + my $csv = new Text::CSV; + + while (<$file>) { + $csv->parse($_); + my @data = $csv->fields; + my $id = $data[$col]; + $id =~ s/\D+//o; + next unless ($id); + push @records, $id; + } + } + } + + my $e = OpenSRF::AppSession->connect('open-ils.cstore'); + $e->request('open-ils.cstore.transaction.begin')->gather(1); + + # still no records ... + my $container = $cgi->param('containerid'); + if ($container) { + my $bucket = $e->request( + 'open-ils.cstore.direct.container.biblio_record_entry_bucket.retrieve', + $container + )->gather(1); + unless($bucket) { + $e->request('open-ils.cstore.transaction.rollback')->gather(1); + $e->disconnect; + $r->log->error("No such bucket $container"); + $logger->error("No such bucket $container"); + return Apache2::Const::NOT_FOUND; + } + my $recs = $e->request( + 'open-ils.cstore.direct.container.biblio_record_entry_bucket_item.search.atomic', + { bucket => $container } + )->gather(1); + @records = map { ($_->target_biblio_record_entry) } @$recs; + } + + unless (@records) { + $e->request('open-ils.cstore.transaction.rollback')->gather(1); + $e->disconnect; + return show_template($r); + } + + # we have a template and some record ids, so... + + # insert the template record + my $min_id = $e->request( + 'open-ils.cstore.json_query', + { select => { bre => [{ column => 'id', transform => 'min', aggregate => 1}] }, from => 'bre' } + )->gather(1)->{id} - 1; + + warn "new template bib id = $min_id\n"; + + my $tmpl_rec = Fieldmapper::biblio::record_entry->new; + $tmpl_rec->id($min_id); + $tmpl_rec->deleted('t'); + $tmpl_rec->active('f'); + $tmpl_rec->marc($template); + $tmpl_rec->creator($usr->id); + $tmpl_rec->editor($usr->id); + + warn "about to create bib $min_id\n"; + $e->request('open-ils.cstore.direct.biblio.record_entry.create', $tmpl_rec )->gather(1); + + # create the new container for the records and the template + my $bucket = Fieldmapper::container::biblio_record_entry_bucket->new; + $bucket->owner($usr->id); + $bucket->btype('template_merge'); + + my $bname = $cgi->param('bname') || 'Temporary Merge Bucket '. localtime() . ' ' . $usr->id; + $bucket->name($bname); + + $bucket = $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket.create', $bucket )->gather(1); + + # create items in the bucket + my $item = Fieldmapper::container::biblio_record_entry_bucket_item->new; + $item->bucket($bucket->id); + $item->target_biblio_record_entry($min_id); + + $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket_item.create', $item )->gather(1); + + for my $r (@records) { + $item->target_biblio_record_entry($r); + $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket_item.create', $item )->gather(1); + } + + $e->request('open-ils.cstore.transaction.commit')->gather(1); + $e->disconnect; + + # fire the background bucket processor + my $cache_key = OpenSRF::AppSession + ->create('open-ils.cat') + ->request('open-ils.cat.container.template_overlay.background', $authid, $bucket->id) + ->gather(1); + + return show_processing_template($r, $bucket->id, \@records, $cache_key); +} + +sub verify_login { + my $auth_token = shift; + return undef unless $auth_token; + + my $user = OpenSRF::AppSession + ->create("open-ils.auth") + ->request( "open-ils.auth.session.retrieve", $auth_token ) + ->gather(1); + + if (ref($user) eq 'HASH' && $user->{ilsevent} == 1001) { + return undef; + } + + return $user if ref($user); + return undef; +} + +sub show_processing_template { + my $r = shift; + my $bid = shift; + my $recs = shift; + my $cache_key = shift; + + my $rec_string = @$recs; + + $r->content_type('text/html'); + $r->print(< + + + Merging records... + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ StatusRecord Count
Success
Failure
+ +
+ + + +HTML + + return Apache2::Const::OK; +} + + +sub show_template { + my $r = shift; + + $r->content_type('text/html'); + $r->print(<<'HTML'); + + + + Merge Template Builder + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
Optional merge queue name:
Batch update records in Bucket: +
+ +
+
or
Batch update records from CSV file:
Column starting from 0
or
Test Ruleset by applying to one record:
+ + + +
+ +
+ + + + + +
Update Template Preview:
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Rule Type + +
MARC Tag +
Optional Subfields
MARC Data
Optionally Restrict By
Subfield +
Regular Expression
+ +
+
+
+
+ + + +HTML + + return Apache2::Const::OK; +} + +1; + + diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index d72ca6d1c7..356839f4a2 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -1780,6 +1780,7 @@ INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('misc INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('staff_client', oils_i18n_gettext('staff_client', 'General Staff Client container', 'cbrebt', 'label')); INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('bookbag', oils_i18n_gettext('bookbag', 'Book Bag', 'cbrebt', 'label')); INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('reading_list', oils_i18n_gettext('reading_list', 'Reading List', 'cbrebt', 'label')); +INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('template_merge',oils_i18n_gettext('template_merge','Template Merge Container', 'cbrebt', 'label')); INSERT INTO container.user_bucket_type (code,label) VALUES ('misc', oils_i18n_gettext('misc', 'Miscellaneous', 'cubt', 'label')); INSERT INTO container.user_bucket_type (code,label) VALUES ('folks', oils_i18n_gettext('folks', 'Friends', 'cubt', 'label')); diff --git a/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql b/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql new file mode 100644 index 0000000000..1a0fd0f5e7 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql @@ -0,0 +1,9 @@ + +BEGIN; + +INSERT INTO config.upgrade_log (version) VALUES ('0434'); + +INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('template_merge','Template Merge Container'); + +COMMIT; + diff --git a/Open-ILS/web/css/theme/default.css b/Open-ILS/web/css/theme/default.css index 2ba93d7d5d..6b59b3099e 100644 --- a/Open-ILS/web/css/theme/default.css +++ b/Open-ILS/web/css/theme/default.css @@ -56,3 +56,4 @@ body { font-size: 80%; background-color:#FFF; } border-bottom:1px solid #ACA899; } +.oils-notify-text { color: red; font-weight:bold; } diff --git a/Open-ILS/web/js/dojo/MARC/Batch.js b/Open-ILS/web/js/dojo/MARC/Batch.js new file mode 100644 index 0000000000..23860378c9 --- /dev/null +++ b/Open-ILS/web/js/dojo/MARC/Batch.js @@ -0,0 +1,82 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2009 Equinox Software, Inc. + * Mike Rylander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource["MARC.Batch"]) { + + dojo.require('dojox.xml.parser'); + dojo.require('MARC.Record'); + + dojo._hasResource["MARC.Batch"] = true; + dojo.provide("MARC.Batch"); + dojo.declare('MARC.Batch', null, { + + constructor : function(kwargs) { + this.ready = false; + this.records = []; + this.source = kwargs.source; + this.delimiter = kwargs.delimiter + this.current_record = 0; + + if (this.source) this.ready = true; + if (!this.ready && kwargs.url) this.fetchURL( kwargs.url ); + + if (this.ready) this.parse(); + }, + + parse : function () { + if (dojo.isObject( this.source )) { // assume an xml collection document + this.source = dojo.query('record', this.source); + this.type = 'xml'; + } else if (this.source.match(/^\s* + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource["MARC.Field"]) { + + dojo._hasResource["MARC.Field"] = true; + dojo.provide("MARC.Field"); + dojo.declare('MARC.Field', null, { + + error : false, // MARC record pointer + record : null, // MARC record pointer + tag : '', // MARC tag + ind1 : '', // MARC indicator 1 + ind2 : '', // MARC indicator 2 + data : '', // MARC data for a controlfield element + subfields : [], // list of MARC subfields for a datafield element + + constructor : function(kwargs) { + this.record = kwargs.record; + this.tag = kwargs.tag; + this.ind1 = kwargs.ind1; + this.ind2 = kwargs.ind2; + this.data = kwargs.data; + if (kwargs.subfields) this.subfields = kwargs.subfields; + else this.subfields = []; + }, + + subfield : function (code) { + var list = dojo.filter( this.subfields, function (s) { + if (s[0] == code) return true; return true; + }); + if (list.length == 1) return list[0]; + return list; + }, + + addSubfields : function () { + for (var i = 0; i < arguments.length; i++) { + var code = arguments[i]; + var value = arguments[++i]; + this.subfields.push( [ code, value ] ); + } + }, + + deleteSubfields : function (c) { + return this.deleteSubfield( { code : c } ); + }, + + deleteSubfield : function (args) { + var me = this; + if (!dojo.isArray( args.code )) { + args.code = [ args.code ]; + } + + if (args.pos && !dojo.isArray( args.pos )) { + args.pos = [ args.pos ]; + } + + for (var i = 0; i < args.code.length; i++) { + var sub_pos = {}; + for (var j = 0; j < me.subfields; j++) { + if (me.subfields[j][0] == args.code[i]) { + + if (!sub_pos[args.code[i]]) sub_pos[args.code[j]] = 0; + else sub_pos[args.code[i]]++; + + if (args.pos) { + for (var k = 0; k < args.pos.length; k++) { + if (sub_pos[args.code[i]] == args.pos[k]) me.subfields.splice(j,1); + } + } else if (args.match && me.subfields[j][1].match( args.match )) { + me.subfields.splice(j,1); + } else { + me.subfields.splice(j,1); + } + } + } + } + }, + + update : function ( args ) { + if (this.isControlfield()) { + this.data = args; + } else { + if (args.ind1) this.ind1 = args.ind1; + if (args.ind2) this.ind2 = args.ind2; + if (args.tag) this.tag = args.tag; + + for (var i in args) { + if (i == 'tag' || i == 'ind1' || i == 'ind2') continue; + var done = 0; + dojo.forEach( this.subfields, function (f) { + if (!done && f[0] == i) { + f[1] = args[i]; + done = 1; + } + }); + } + } + }, + + isControlfield : function () { + return this.tag < '010' ? true : false; + }, + + indicator : function (num, value) { + if (value) { + if (num == 1) this.ind1 = value; + else if (num == 2) this.ind2 = value; + else { this.error = true; return null; } + } + if (num == 1) return this.ind1; + else if (num == 2) return this.ind2; + else { this.error = true; return null; } + } + + }); +} diff --git a/Open-ILS/web/js/dojo/MARC/Record.js b/Open-ILS/web/js/dojo/MARC/Record.js new file mode 100644 index 0000000000..f9385502bc --- /dev/null +++ b/Open-ILS/web/js/dojo/MARC/Record.js @@ -0,0 +1,299 @@ +/* --------------------------------------------------------------------------- + * Copyright (C) 2009 Equinox Software, Inc. + * Mike Rylander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * --------------------------------------------------------------------------- + */ + +if(!dojo._hasResource["MARC.Record"]) { + + dojo.require('dojox.xml.parser'); + dojo.require('MARC.Field'); + + dojo._hasResource["MARC.Record"] = true; + dojo.provide("MARC.Record"); + dojo.declare('MARC.Record', null, { + + delimiter : '\u2021', // default subfield delimiter + + constructor : function(kwargs) { + this.fields = []; + this.leader = '00000cam a2200205Ka 4500'; + + if (kwargs.delimiter) this.delimiter = kwargs.delimiter; + if (kwargs.onLoad) this.onLoad = kwargs.onLoad; + if (kwargs.url) { + this.fromXmlURL(kwargs.url); + } else if (kwargs.marcxml) { + this.fromXmlString(kwargs.marcxml); + if (this.onLoad) this.onLoad(); + } else if (kwargs.xml) { + this.fromXmlDocument(kwargs.xml); + if (this.onLoad) this.onLoad(); + } else if (kwargs.marcbreaker) { + this.fromBreaker(kwargs.marcbreaker); + if (this.onLoad) this.onLoad(); + } + }, + + title : function () { return this.subfield('245','a') }, + + field : function (spec) { + var list = dojo.filter( this.fields, function (f) { + if (f.tag.match(spec)) return true; + return false; + }); + + if (list.length == 1) return list[0]; + return list; + }, + + subfield : function (spec, code) { return this.field(spec)[0].subfield(code) }, + + appendFields : function () { + var me = this; + dojo.forEach( arguments, function (f) { me.fields.push( f ) } ); + }, + + deleteField : function (f) { return this.deleteFields(f) }, + + insertOrderedFields : function () { + var me = this; + for (var i = 0; i < arguments.length; i++) { + var f = arguments[i]; + var done = false; + for (var j = 0; j < this.fields.length; j++) { + if (f.tag < this.fields[j].tag) { + this.insertFieldsBefore(this.fields[j], f); + done = true; + break; + } + } + if (!done) this.appendFields(f); + } + }, + + insertFieldsBefore : function (target) { + var args = Array.prototype.slice.call(arguments); + args.splice(0,1); + var me = this; + for (var j = 0; j < this.fields.length; j++) { + if (target === this.fields[j]) { + j--; + dojo.forEach( args, function (f) { + me.fields.splice(j++,0,f); + }); + break; + } + } + }, + + insertFieldsAfter : function (target) { + var args = Array.prototype.slice.call(arguments); + args.splice(0,1); + var me = this; + for (var j = 0; j < this.fields.length; j++) { + if (target === this.fields[j]) { + dojo.forEach( args, function (f) { + me.fields.splice(j++,0,f); + }); + break; + } + } + }, + + deleteFields : function () { + var me = this; + var counter = 0; + for ( var i in arguments ) { + var f = arguments[i]; + for (var j = 0; j < me.fields.length; j++) { + if (f === me.fields[j]) { + me.fields[j].record = null; + me.fields.splice(j,0); + counter++ + break; + } + } + } + return counter; + }, + + clone : function () { return dojo.clone(this) }, + + fromXmlURL : function (url) { + this.ready = false; + var me = this; + dojo.xhrGet({ + url : url, + sync : true, + handleAs: 'xml', + load : function (mxml) { + me.fromXmlDocument(dojo.query('record', mxml)[0]); + me.ready = true; + if (me.onLoad) me.onLoad(); + } + }); + }, + + fromXmlString : function (mxml) { + return this.fromXmlDocument( dojox.xml.parser.parse( mxml ) ); + }, + + fromXmlDocument : function (mxml) { + var me = this; + me.leader = dojox.xml.parser.textContent(dojo.query('leader', mxml)[0]) || '00000cam a2200205Ka 4500'; + + dojo.forEach( dojo.query('controlfield', mxml), function (cf) { + me.fields.push( + new MARC.Field({ + record : me, + tag : cf.getAttribute('tag'), + data : dojox.xml.parser.textContent(cf) + }) + ) + }); + + dojo.forEach( dojo.query('datafield', mxml), function (df) { + me.fields.push( + new MARC.Field({ + record : me, + tag : df.getAttribute('tag'), + ind1 : df.getAttribute('ind1'), + ind2 : df.getAttribute('ind2'), + subfields : dojo.map( + dojo.query('subfield', df), + function (sf) { + return [ sf.getAttribute('code'), dojox.xml.parser.textContent(sf) ]; + } + ) + }) + ) + }); + + return this; + }, + + toXmlDocument : function () { + + var doc = dojox.xml.parser.parse(''); + var rec_node = dojo.query('record', doc)[0]; + + var ldr = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'leader'); + dojox.xml.parser.textContent(ldr, this.leader); + rec_node.appendChild( ldr ); + + dojo.forEach( this.fields, function (f) { + var element = f.isControlfield() ? 'controlfield' : 'datafield'; + var f_node = doc.createElementNS( 'http://www.loc.gov/MARC21/slim', element ); + f_node.setAttribute('tag', f.tag); + + if (f.isControlfield() && f.data) { + dojox.xml.parser.textContent(f_node, f.data); + } else { + f_node.setAttribute('ind1', f.indicator(1)); + f_node.setAttribute('ind2', f.indicator(2)); + dojo.forEach( f.subfields, function (sf) { + var sf_node = doc.createElementNS('http://www.loc.gov/MARC21/slim', 'subfield'); + sf_node.setAttribute('code', sf[0]); + dojox.xml.parser.textContent(sf_node, sf[1]); + f_node.appendChild(sf_node); + }); + } + + rec_node.appendChild(f_node); + }); + + return doc; + }, + + toXmlString : function () { + return dojox.xml.parser.innerXML( this.toXmlDocument() ); + }, + + fromBreaker : function (marctxt) { + var me = this; + + function cf_line_data (l) { return l.substring(4) }; + function df_line_data (l) { return l.substring(6) }; + function line_tag (l) { return l.substring(0,3) }; + function df_ind1 (l) { return l.substring(4,5).replace('\\',' ') }; + function df_ind2 (l) { return l.substring(5,6).replace('\\',' ') }; + function isControlField (l) { + var x = line_tag(l); + return (x == 'LDR' || x < '010') ? true : false; + } + + var lines = marctxt.replace(/^=/gm,'').split('\n'); + dojo.forEach(lines, function (current_line) { + + if (current_line.match(/^#/)) { + // skip comment lines + } else if (isControlField(current_line)) { + if (line_tag(current_line) == 'LDR') { + me.leader = cf_line_data(current_line) || '00000cam a2200205Ka 4500'; + } else { + me.fields.push( + new MARC.Field({ + record : me, + tag : line_tag(current_line), + data : cf_line_data(current_line).replace('\\',' ','g') + }) + ); + } + } else { + var data = df_line_data(current_line); + if (!(data.substring(0,1) == me.delimiter)) data = me.delimiter + 'a' + data; + + var sf_list = data.split(me.delimiter); + sf_list.shift(); + + me.fields.push( + new MARC.Field({ + record : me, + tag : line_tag(current_line), + ind1 : df_ind1(current_line), + ind2 : df_ind2(current_line), + subfields : dojo.map( + sf_list, + function (sf) { return [ sf.substring(0,1), sf.substring(1) ] } + ) + }) + ); + } + }); + + return this; + }, + + toBreaker : function () { + + var me = this; + var mtxt = '=LDR ' + this.leader + '\n'; + + mtxt += dojo.map( this.fields, function (f) { + if (f.isControlfield() && f.data) { + return '=' + f.tag + ' ' + f.data.replace(' ','\\','g'); + } else { + return '=' + f.tag + ' ' + + f.indicator(1).replace(' ','\\') + + f.indicator(2).replace(' ','\\') + + dojo.map( f.subfields, function (sf) { + return me.delimiter + sf.join(''); + }).join(''); + } + }).join('\n'); + + return mtxt; + } + }); +} diff --git a/Open-ILS/web/js/dojo/fieldmapper/AutoIDL.js b/Open-ILS/web/js/dojo/fieldmapper/AutoIDL.js index f776a200c9..008011af00 100644 --- a/Open-ILS/web/js/dojo/fieldmapper/AutoIDL.js +++ b/Open-ILS/web/js/dojo/fieldmapper/AutoIDL.js @@ -2,6 +2,14 @@ if(!dojo._hasResource["fieldmapper.AutoIDL"]) { dojo.provide("fieldmapper.AutoIDL"); dojo.require("fieldmapper.IDL"); - fieldmapper.IDL.load(); + + var classlist = []; + try { + classlist = dojo.config.AutoIDL || []; + } catch(x) { + /* meh */ + } + + fieldmapper.IDL.load(classlist); } diff --git a/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js b/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js index 901d87d084..5ef69d750f 100644 --- a/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js +++ b/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js @@ -53,7 +53,7 @@ if(!dojo._hasResource["fieldmapper.Fieldmapper"]){ obj.a[i] = thing.clone(); } else { - if(instanceOf(thing, Array)) { + if(dojo.isArray(thing)) { obj.a[i] = new Array(); for( var j in thing ) { @@ -71,11 +71,103 @@ if(!dojo._hasResource["fieldmapper.Fieldmapper"]){ return obj; }, + RequiredField : function (f) { + if (!f) return; + if (fieldmapper.IDL && fieldmapper.IDL.loaded) + return this.Structure.fields[f].required; + return; + }, + + ValidateField : function (f) { + if (!f) return; + if (fieldmapper.IDL && fieldmapper.IDL.loaded) { + if (this.Structure.fields[f] && this.Structure.fields[f].validate) { + return this.Structure.fields[f].validate.test(this[f]()); + } + return true; + } + return; + } + + + +/* isnew : function(n) { if(arguments.length == 1) this.a[0] =n; return this.a[0]; }, ischanged : function(n) { if(arguments.length == 1) this.a[1] =n; return this.a[1]; }, isdeleted : function(n) { if(arguments.length == 1) this.a[2] =n; return this.a[2]; } +*/ + }); + fieldmapper.vivicateClass = function (cl) { + dojo.provide( cl ); + dojo.declare( cl , fieldmapper.Fieldmapper, { + constructor : function () { + if (!this.a) this.a = []; + this.classname = this.declaredClass; + this._fields = []; + + if (fieldmapper.IDL && fieldmapper.IDL.loaded) { + this.Structure = fieldmapper.IDL.fmclasses[this.classname] + + for (var f in fieldmapper.IDL.fmclasses[this.classname].fields) { + var field = fieldmapper.IDL.fmclasses[this.classname].fields[f]; + var p = field.array_position; + this._fields.push( field.name ); + this[field.name]=new Function('n', 'if(arguments.length==1)this.a['+p+']=n;return this.a['+p+'];'); + } + } else { + this._fields = fmclasses[this.classname]; + + for( var pos = 0; pos < this._fields.length; pos++ ) { + var p = parseInt(pos); + var f = this._fields[pos]; + this[f]=new Function('n', 'if(arguments.length==1)this.a['+p+']=n;return this.a['+p+'];'); + } + } + + } + }); + fieldmapper[cl] = window[cl]; // alias into place + if (fieldmapper.IDL && fieldmapper.IDL.loaded) fieldmapper[cl].Identifier = fieldmapper.IDL.fmclasses[cl].pkey; + }; + + if (!window.fmclasses) dojo.require("fieldmapper.fmall", true); + for( var cl in fmclasses ) { + fieldmapper.vivicateClass(cl); + } + + // if we were NOT called by the IDL loader ... + // XXX This is now deprecated in preference to fieldmapper.AutoIDL + if ( !(fieldmapper.IDL && fieldmapper.IDL.loaded) ) { + + fieldmapper.cmsa.Identifier = 'alias'; + fieldmapper.cmc.Identifier = 'name'; + fieldmapper.i18n_l.Identifier = 'code'; + fieldmapper.ccpbt.Identifier = 'code'; + fieldmapper.ccnbt.Identifier = 'code'; + fieldmapper.cbrebt.Identifier = 'code'; + fieldmapper.cubt.Identifier = 'code'; + fieldmapper.ccm.Identifier = 'code'; + fieldmapper.cvrfm.Identifier = 'code'; + fieldmapper.clm.Identifier = 'code'; + fieldmapper.cam.Identifier = 'code'; + fieldmapper.cifm.Identifier = 'code'; + fieldmapper.citm.Identifier = 'code'; + fieldmapper.cblvl.Identifier = 'code'; + fieldmapper.clfm.Identifier = 'code'; + fieldmapper.mous.Identifier = 'usr'; + fieldmapper.moucs.Identifier = 'usr'; + fieldmapper.mucs.Identifier = 'usr'; + fieldmapper.mus.Identifier = 'usr'; + fieldmapper.rxbt.Identifier = 'xact'; + fieldmapper.rxpt.Identifier = 'xact'; + fieldmapper.cxt.Identifier = 'name'; + fieldmapper.amtr.Identifier = 'matchpoint'; + fieldmapper.coust.Identifier = 'name'; + + } + fieldmapper._request = function ( meth, staff, params ) { var ses = OpenSRF.CachedClientSession( meth[0] ); if (!ses) return null; @@ -90,7 +182,7 @@ if(!dojo._hasResource["fieldmapper.Fieldmapper"]){ if (dojo.isObject(params)) { args = params; } else { - args.params = [].splice.call(arguments, 1, arguments.length - 1); + args.params = [].splice.call(arguments, 2, arguments.length - 2); } } @@ -136,76 +228,6 @@ if(!dojo._hasResource["fieldmapper.Fieldmapper"]){ fieldmapper.staffRequest = function (meth, params) { return fieldmapper._request(meth, true, params) }; fieldmapper.Fieldmapper.prototype.staffRequest = fieldmapper.staffRequest; - // if we were called by the IDL loader ... - if ( fieldmapper.IDL && fieldmapper.IDL.loaded ) { - for( var cl in fieldmapper.IDL.fmclasses ) { - dojo.provide( cl ); - dojo.declare( cl , fieldmapper.Fieldmapper, { - constructor : function () { - if (!this.a) this.a = []; - this.classname = this.declaredClass; - this._fields = []; - this.Structure = fieldmapper.IDL.fmclasses[this.classname] - - for (var f in fieldmapper.IDL.fmclasses[this.classname].fields) { - var field = fieldmapper.IDL.fmclasses[this.classname].fields[f]; - var p = field.array_position; - this._fields.push( field.name ); - this[field.name]=new Function('n', 'if(arguments.length==1)this.a['+p+']=n;return this.a['+p+'];'); - } - } - }); - fieldmapper[cl] = window[cl]; // alias into place - fieldmapper[cl].Identifier = fieldmapper.IDL.fmclasses[cl].pkey; - } - - // ... otherwise we need to get the oldschool fmall.js stuff, which will lack .structure - } else { - if (!window.fmclasses) - dojo.require("fieldmapper.fmall", true); - - for( var cl in fmclasses ) { - dojo.provide( cl ); - dojo.declare( cl , fieldmapper.Fieldmapper, { - constructor : function () { - if (!this.a) this.a = []; - this.classname = this.declaredClass; - this._fields = fmclasses[this.classname]; - for( var pos = 0; pos < this._fields.length; pos++ ) { - var p = parseInt(pos); - var f = this._fields[pos]; - this[f]=new Function('n', 'if(arguments.length==1)this.a['+p+']=n;return this.a['+p+'];'); - } - } - }); - fieldmapper[cl] = window[cl]; // alias into place - fieldmapper[cl].Identifier = 'id'; // alias into place - } - - fieldmapper.i18n_l.Identifier = 'code'; - fieldmapper.ccpbt.Identifier = 'code'; - fieldmapper.ccnbt.Identifier = 'code'; - fieldmapper.cbrebt.Identifier = 'code'; - fieldmapper.cubt.Identifier = 'code'; - fieldmapper.ccm.Identifier = 'code'; - fieldmapper.cvrfm.Identifier = 'code'; - fieldmapper.clm.Identifier = 'code'; - fieldmapper.cam.Identifier = 'code'; - fieldmapper.cifm.Identifier = 'code'; - fieldmapper.citm.Identifier = 'code'; - fieldmapper.cblvl.Identifier = 'code'; - fieldmapper.clfm.Identifier = 'code'; - fieldmapper.mous.Identifier = 'usr'; - fieldmapper.moucs.Identifier = 'usr'; - fieldmapper.mucs.Identifier = 'usr'; - fieldmapper.mus.Identifier = 'usr'; - fieldmapper.rxbt.Identifier = 'xact'; - fieldmapper.rxpt.Identifier = 'xact'; - fieldmapper.cxt.Identifier = 'name'; - fieldmapper.amtr.Identifier = 'matchpoint'; - - } - fieldmapper.OpenSRF = {}; /* Methods are defined as [ service, method, have_staff ] @@ -291,7 +313,7 @@ if(!dojo._hasResource["fieldmapper.Fieldmapper"]){ FETCH_MR_DESCRIPTORS : ['open-ils.search','open-ils.search.metabib.record_to_descriptors'], FETCH_HIGHEST_PERM_ORG : ['open-ils.actor','open-ils.actor.user.perm.highest_org.batch'], FETCH_USER_NOTES : ['open-ils.actor','open-ils.actor.note.retrieve.all'], - FETCH_ORG_BY_SHORTNAME : ['open-ils.actor','open-ils.actor.org_unit.retrieve_by_shorname'], + FETCH_ORG_BY_SHORTNAME : ['open-ils.actor','open-ils.actor.org_unit.retrieve_by_shortname'], FETCH_BIB_ID_BY_BARCODE : ['open-ils.search','open-ils.search.bib_id.by_barcode'], FETCH_ORG_SETTING : ['open-ils.actor','open-ils.actor.ou_setting.ancestor_default'], FETCH_ORG_SETTING_BATCH : ['open-ils.actor','open-ils.actor.ou_setting.ancestor_default.batch'] diff --git a/Open-ILS/web/js/dojo/fieldmapper/IDL.js b/Open-ILS/web/js/dojo/fieldmapper/IDL.js index 87af3e3650..2c3f1baab1 100644 --- a/Open-ILS/web/js/dojo/fieldmapper/IDL.js +++ b/Open-ILS/web/js/dojo/fieldmapper/IDL.js @@ -1,45 +1,62 @@ if(!dojo._hasResource["fieldmapper.IDL"]) { + dojo.require("DojoSRF"); dojo.provide("fieldmapper.IDL"); dojo.declare('fieldmapper.IDL', null, { _URL_PATH : '/reports/fm_IDL.xml', // XXX locale? - // -- just need to set up xmlent and use '/reports/'+dojo.locale+'/fm_IDL.xml' + // -- just need to set up xmlent and use '/reports/'+OpenSRF.locale+'/fm_IDL.xml' NS_REPORTS : 'http://open-ils.org/spec/opensrf/IDL/reporter/v1', NS_PERSIST : 'http://open-ils.org/spec/opensrf/IDL/persistence/v1', NS_OBJ : 'http://open-ils.org/spec/opensrf/IDL/objects/v1', - constructor : function(callback, force) { - if(!fieldmapper.IDL.fmclasses || force) { + constructor : function(classlist) { + + if(!fieldmapper.IDL.fmclasses || (classlist && classlist.length)) { + var idl_url = this._URL_PATH; + + if (classlist.length) { + idl_url += '?'; + + for (var i = 0; i < classlist.length; i++) { + var trim_class = classlist[i]; + if (!trim_class) continue; + + if (i > 0) idl_url += '&'; + idl_url += 'class=' + trim_class; + } + } + var self = this; dojo.xhrGet({ - url : this._URL_PATH, + url : idl_url, handleAs : 'xml', sync : true, timeout : 10000, load : function (response) { - self._parse(response, callback); + self._parse(response); fieldmapper.IDL.loaded = true; }, error : function (response) { fieldmapper.IDL.loaded = false; dojo.require('fieldmapper.fmall', true); - if(callback) - callback(); } }); } + dojo.require('fieldmapper.Fieldmapper'); - return dojo.require('fieldmapper.Fieldmapper'); + if (classlist && classlist.length) + dojo.forEach( classlist, function (c) { fieldmapper.vivicateClass(c); } ); }, - _parse : function(xmlNode, callback) { - var classes = xmlNode.getElementsByTagName('class'); - var idl = fieldmapper.IDL.fmclasses = {}; - + _parse : function(xmlNode) { + var classes = dojo.query('class',xmlNode); + if (!fieldmapper.IDL || !fieldmapper.IDL.fmclasses) + fieldmapper.IDL.fmclasses = {}; + for(var i = 0; i < classes.length; i++) { var node = classes[i]; var id = node.getAttribute('id'); - var fields = node.getElementsByTagName('fields')[0]; + var fields = dojo.query('fields',node)[0]; window.fmclasses[id] = []; var fieldData = this._parseFields(node, id); @@ -48,22 +65,25 @@ if(!dojo._hasResource["fieldmapper.IDL"]) { fields : fieldData.list, field_map : fieldData.map, name : node.getAttribute('id'), - //table : node.getAttributeNS(this.NS_PERSIST, 'tablename'), - //core : node.getAttributeNS(this.NS_REPORTS, 'core'), - label : node.getAttributeNS(this.NS_REPORTS, 'label'), - restrict_primary : node.getAttributeNS(this.NS_PERSIST, 'restrict_primary'), - virtual : (node.getAttributeNS(this.NS_PERSIST, 'virtual') == 'true'), - pkey : fields.getAttributeNS(this.NS_PERSIST, 'primary'), - pkey_sequence : fields.getAttributeNS(this.NS_PERSIST, 'sequence') + //table : fieldmapper._getAttributeNS(node,this.NS_PERSIST, 'tablename'), + //core : fieldmapper._getAttributeNS(node,this.NS_REPORTS, 'core'), + label : fieldmapper._getAttributeNS(node,this.NS_REPORTS, 'label'), + restrict_primary : fieldmapper._getAttributeNS(node,this.NS_PERSIST, 'restrict_primary'), + virtual : (fieldmapper._getAttributeNS(node,this.NS_PERSIST, 'virtual') == 'true'), + pkey : fieldmapper._getAttributeNS(fields,this.NS_PERSIST, 'primary'), + pkey_sequence : fieldmapper._getAttributeNS(fields,this.NS_PERSIST, 'sequence') }; - var permacrud = node.getElementsByTagName('permacrud')[0]; + var valid = fieldmapper._getAttributeNS(node,this.NS_OBJ, 'validate'); + if (valid) obj.validate = new RegExp( valid.replace(/\\/g, '\\\\') ); + + var permacrud = dojo.query('permacrud',node)[0]; if(permacrud) { var actions = ['create', 'retrieve', 'update', 'delete']; obj.permacrud = {}; for(var idx in actions) { var action = actions[idx]; - var pnode = permacrud.getElementsByTagName(action)[0]; + var pnode = dojo.query(action,permacrud)[0]; if(pnode) { var permString = pnode.getAttribute('permission'); var permList = null; @@ -85,11 +105,9 @@ if(!dojo._hasResource["fieldmapper.IDL"]) { obj.core = (obj.core == 'true'); obj.label = (obj.label) ? obj.label : obj.name; - idl[id] = obj; + fieldmapper.IDL.fmclasses[id] = obj; } - if(callback) - callback(); }, /* parses the links and fields portion of the IDL */ @@ -97,11 +115,11 @@ if(!dojo._hasResource["fieldmapper.IDL"]) { var data = []; var map = {}; - var fields = node.getElementsByTagName('fields')[0]; - fields = fields.getElementsByTagName('field'); + var fields = dojo.query('fields',node)[0]; + fields = dojo.query('field',fields); - var links = node.getElementsByTagName('links')[0]; - if( links ) links = links.getElementsByTagName('link'); + var links = dojo.query('links',node)[0]; + if( links ) links = dojo.query('link',links); else links = []; @@ -116,13 +134,15 @@ if(!dojo._hasResource["fieldmapper.IDL"]) { var obj = { field : field, name : name, - label : field.getAttributeNS(this.NS_REPORTS,'label'), - datatype : field.getAttributeNS(this.NS_REPORTS,'datatype'), - primitive : field.getAttributeNS(this.NS_PERSIST,'primitive'), - selector : field.getAttributeNS(this.NS_REPORTS,'selector'), + label : fieldmapper._getAttributeNS(field,this.NS_REPORTS,'label'), + datatype : fieldmapper._getAttributeNS(field,this.NS_REPORTS,'datatype'), + primitive : fieldmapper._getAttributeNS(field,this.NS_PERSIST,'primitive'), + selector : fieldmapper._getAttributeNS(field,this.NS_REPORTS,'selector'), array_position : position++, type : 'field', - virtual : (fields[i].getAttributeNS(this.NS_PERSIST, 'virtual') == 'true') + virtual : (fieldmapper._getAttributeNS(fields[i],this.NS_PERSIST, 'virtual') == 'true'), + required : (fieldmapper._getAttributeNS(fields[i],this.NS_OBJ, 'required') == 'true'), + i18n : (fieldmapper._getAttributeNS(fields[i],this.NS_PERSIST, 'i18n') == 'true') }; obj.label = obj.label || obj.name; @@ -167,9 +187,20 @@ if(!dojo._hasResource["fieldmapper.IDL"]) { }); + fieldmapper._getAttributeNS = function (node,ns,attr) { + if (node.getAttributeNS) return node.getAttributeNS(ns,attr); + return node.getAttribute(attr); + }; + window.fmclasses = {}; - fieldmapper.IDL.load = function (callback, force) { return new fieldmapper.IDL(callback, force); }; + fieldmapper.IDL.load = function (list) { if (!list) list = []; return new fieldmapper.IDL(list); }; fieldmapper.IDL.loaded = false; + JSON2js.fallbackObjectifier = function (arg, key_name, val_name) { + console.log("Firing IDL loader for " + arg[key_name]); + fieldmapper.IDL.load([arg[key_name]]); + return decodeJS(arg); + } + } diff --git a/Open-ILS/web/js/dojo/fieldmapper/dojoData.js b/Open-ILS/web/js/dojo/fieldmapper/dojoData.js index 597e9b127e..9b0cddd3c6 100644 --- a/Open-ILS/web/js/dojo/fieldmapper/dojoData.js +++ b/Open-ILS/web/js/dojo/fieldmapper/dojoData.js @@ -26,10 +26,10 @@ if(!dojo._hasResource['fieldmapper.dojoData']){ function _fromStoreItem (data) { this.fromHash(data); - for (var i in this._ignore_fields) + for (var i = 0; this._ignore_fields && i < this._ignore_fields.length; i++) this[this._ignore_fields[i]](null); - for ( var i=0; i < this._fields.length; i++) { + for (var i = 0; this._fields && i < this._fields.length; i++) { if (dojo.isArray( this[this._fields[i]]() )) this[this._fields[i]]( this[this._fields[i]]()[0] ); } @@ -53,22 +53,22 @@ if(!dojo._hasResource['fieldmapper.dojoData']){ if (!params) params = {}; var data = this.initStoreData(label, params); - for (var i in list) data.items.push( list[i].toHash(true, params.virtualFields) ); + for (var i = 0; list && i < list.length; i++) data.items.push( list[i].toHash(true, params.virtualFields) ); if (params.children && params.parent) { var _hash_list = data.items; var _find_root = {}; - for (var i in _hash_list) { + for (var i = 0; _hash_list && i < _hash_list.length; i++) { _find_root[_hash_list[i][params.identifier]] = _hash_list[i]; } var item_data = []; - for (var i in _hash_list) { + for (var i = 0; _hash_list && i < _hash_list.length; i++) { var obj = _hash_list[i] obj[params.children] = []; - for (var j in _hash_list) { + for (var j = 0; _hash_list && j < _hash_list.length; j++) { var kid = _hash_list[j]; if (kid[params.parent] == obj[params.identifier]) { obj[params.children].push( { _reference : kid[params.identifier] } ); @@ -92,15 +92,18 @@ if(!dojo._hasResource['fieldmapper.dojoData']){ return data; } - for (var i in fmclasses) fieldmapper[i].prototype.fromStoreItem = _fromStoreItem; - for (var i in fmclasses) fieldmapper[i].toStoreData = _toStoreData; - for (var i in fmclasses) fieldmapper[i].toStoreItem = _toStoreItem; - for (var i in fmclasses) fieldmapper[i].prototype.toStoreItem = function ( args ) { return _toStoreItem(this, args) }; - for (var i in fmclasses) fieldmapper[i].initStoreData = _initStoreData; + for (var i in fmclasses) { + fieldmapper[i].prototype.fromStoreItem = _fromStoreItem; + fieldmapper[i].prototype.fromStoreItem = _fromStoreItem; + fieldmapper[i].toStoreData = _toStoreData; + fieldmapper[i].toStoreItem = _toStoreItem; + fieldmapper[i].prototype.toStoreItem = function ( args ) { return _toStoreItem(this, args) }; + fieldmapper[i].initStoreData = _initStoreData; + } - fieldmapper.aou.prototype._ignore_fields = ['children']; - fieldmapper.aout.prototype._ignore_fields = ['children']; - fieldmapper.pgt.prototype._ignore_fields = ['children']; + if (fieldmapper.aou) fieldmapper.aou.prototype._ignore_fields = ['children']; + if (fieldmapper.aout) fieldmapper.aout.prototype._ignore_fields = ['children']; + if (fieldmapper.pgt) fieldmapper.pgt.prototype._ignore_fields = ['children']; fieldmapper.aou.toStoreData = function (list, label) { if (!label) label = 'shortname'; diff --git a/Open-ILS/web/js/dojo/fieldmapper/hash.js b/Open-ILS/web/js/dojo/fieldmapper/hash.js index d3a62568c6..c11169ad2e 100644 --- a/Open-ILS/web/js/dojo/fieldmapper/hash.js +++ b/Open-ILS/web/js/dojo/fieldmapper/hash.js @@ -41,7 +41,7 @@ if(!dojo._hasResource['fieldmapper.hash']){ } if (virtFields && virtFields.length > 0) { - for (var i in virtFields) { + for (var i = 0; i < virtFields.length; i++) { if (!_hash[virtFields[i]]) _hash[virtFields[i]] = null; } diff --git a/Open-ILS/web/js/dojo/openils/User.js b/Open-ILS/web/js/dojo/openils/User.js index 11ca1a5df7..fb3dd9d167 100644 --- a/Open-ILS/web/js/dojo/openils/User.js +++ b/Open-ILS/web/js/dojo/openils/User.js @@ -24,6 +24,7 @@ if(!dojo._hasResource["openils.User"]) { dojo.require('fieldmapper.Fieldmapper'); dojo.require('fieldmapper.OrgUtils'); dojo.require('openils.Util'); + dojo.require('dojo.cookie'); dojo.declare('openils.User', null, { @@ -50,6 +51,7 @@ if(!dojo._hasResource["openils.User"]) { this.authcookie = kwargs.authcookie || openils.User.authcookie; this.permOrgStoreCache = {}; /* permName => permOrgUnitStore map */ + if (this.authcookie) this.authtoken = dojo.cookie(this.authcookie); if (this.id && this.authtoken) this.user = this.getById( this.id ); else if (this.authtoken) this.getBySession(); else if (kwargs.login) this.login(); @@ -137,13 +139,16 @@ if(!dojo._hasResource["openils.User"]) { var authReq = OpenSRF.CachedClientSession('open-ils.auth').request('open-ils.auth.authenticate.complete', loginInfo); authReq.oncomplete = function(rr) { var data = rr.recv().content(); + + if(!data || !data.payload) + throw new Error("Login Failed: " + js2JSON(data)); + _u.authtoken = data.payload.authtoken; if (!openils.User.authtoken) openils.User.authtoken = _u.authtoken; _u.authtime = data.payload.authtime; if (!openils.User.authtime) openils.User.authtime = _u.authtime; _u.getBySession(onComplete); if(_u.authcookie) { - dojo.require('dojo.cookie'); dojo.cookie(_u.authcookie, _u.authtoken, {path:'/'}); } } @@ -179,15 +184,18 @@ if(!dojo._hasResource["openils.User"]) { [loginInfo] ); + if(!data || !data.payload) return false; + _u.authtoken = data.payload.authtoken; if (!openils.User.authtoken) openils.User.authtoken = _u.authtoken; _u.authtime = data.payload.authtime; if (!openils.User.authtime) openils.User.authtime = _u.authtime; if(_u.authcookie) { - dojo.require('dojo.cookie'); dojo.cookie(_u.authcookie, _u.authtoken, {path:'/'}); } + + return true; }, diff --git a/Open-ILS/web/js/ui/base.js b/Open-ILS/web/js/ui/base.js index d9dfc83489..cd723bfe23 100644 --- a/Open-ILS/web/js/ui/base.js +++ b/Open-ILS/web/js/ui/base.js @@ -31,6 +31,7 @@ function oilsSetupUser() { } function oilsDoLogin() { + openils.Util.hide('oils-login-failed'); var cgi = new openils.CGI(); var workstation = cgi.param('ws') || dojo.cookie('ws'); var user = new openils.User(); @@ -41,9 +42,14 @@ function oilsDoLogin() { }; if(workstation) args.workstation = workstation; - user.login(args); - dojo.cookie('ses', user.authtoken, {path : '/'}); - location.href = location.href; + + if(user.login(args)) { + dojo.cookie('ses', user.authtoken, {path : '/'}); + location.href = location.href; + } else { + openils.Util.show('oils-login-failed'); + } + return false; } diff --git a/Open-ILS/web/templates/login.tt2 b/Open-ILS/web/templates/login.tt2 index faf119a012..2c8d9713a8 100644 --- a/Open-ILS/web/templates/login.tt2 +++ b/Open-ILS/web/templates/login.tt2 @@ -2,6 +2,7 @@