[open-ils-commits] r12228 - in branches/staff-client-experiment: . Open-ILS/examples Open-ILS/src Open-ILS/src/c-apps Open-ILS/src/extras Open-ILS/src/perlmods/OpenILS/Application Open-ILS/src/perlmods/OpenILS/Application/Actor Open-ILS/src/perlmods/OpenILS/Application/Circ Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher Open-ILS/src/perlmods/OpenILS/Application/Trigger Open-ILS/src/perlmods/OpenILS/Utils Open-ILS/src/perlmods/OpenILS/Utils/MFHD Open-ILS/src/sql/Pg Open-ILS/tests/datasets Open-ILS/web/conify/global Open-ILS/web/css/skin Open-ILS/web/js/dojo/fieldmapper Open-ILS/web/js/dojo/openils Open-ILS/web/js/dojo/openils/acq Open-ILS/web/js/dojo/openils/widget Open-ILS/web/js/ui/default Open-ILS/web/js/ui/default/acq/financial Open-ILS/web/js/ui/default/conify/global Open-ILS/web/js/ui/default/conify/global/config Open-ILS/web/opac/common/js Open-ILS/web/opac/images Open-ILS/web/opac/images/slimtree Open-ILS/web/opac/images/tor Open-ILS/web/opac/skin Open-ILS/web/opac/skin/default/js Open-ILS/web/opac/skin/default/xml/common Open-ILS/web/opac/theme Open-ILS/web/templates Open-ILS/web/templates/default Open-ILS/web/templates/default/acq/financial Open-ILS/web/templates/default/conify/global Open-ILS/web/templates/default/conify/global/config Open-ILS/xul/staff_client/chrome/content/main Open-ILS/xul/staff_client/server/cat Open-ILS/xul/staff_client/server/patron (phasefx)

svn at svn.open-ils.org svn at svn.open-ils.org
Thu Feb 19 00:39:56 EST 2009


Author: phasefx
Date: 2009-02-19 00:39:53 -0500 (Thu, 19 Feb 2009)
New Revision: 12228

Added:
   branches/staff-client-experiment/Open-ILS/src/c-apps/test_json_query.c
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm
   branches/staff-client-experiment/Open-ILS/tests/datasets/nepali.marc
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/XUL.js
   branches/staff-client-experiment/Open-ILS/web/js/ui/default/actor/
   branches/staff-client-experiment/Open-ILS/web/js/ui/default/conify/global/acq/
   branches/staff-client-experiment/Open-ILS/web/js/ui/default/conify/global/config/idl_field_doc.js
   branches/staff-client-experiment/Open-ILS/web/opac/images/Thumbs.db
   branches/staff-client-experiment/Open-ILS/web/opac/images/advancedsearch-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/advancedsearch-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/blank.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/book-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/cartographic.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/chooselibrary-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/chooselibrary-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/content-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/content-bg.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/details-f-bg-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/details-f-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/details-headers-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/earth-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/eg_tiny_logo.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-bl.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-bottom.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-br.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-corners.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-left.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-right.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-tl.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-top.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/footer-tr.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/header-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/header-shadow.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/home-bottom-tag-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/lg-txt.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/libselect-btn.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/list-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/list-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/loginas-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/loginas-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/logo.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/logo.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/mix-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/mixed material.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/mov-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/moving image.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/mussymbol-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/myaccount-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/myaccount-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/noimg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/nonmusic-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/notated music.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/pic-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/placeholder-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/progressbar_green-old.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/recsound-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/reg-txt.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/search-btn.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/searchbar-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/searchbox-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/sidebar-bg.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/small-rss.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/software, multimedia.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/software-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/sound recording-musical.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/sound recording-nonmusical.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/sound recording.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/sound-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/still images.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/subject-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/subject-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/text.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/three dimensional object.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/threed-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon-u.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon-u.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/book-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/book-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/earth-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/earth-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mix-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mix-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mov-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mov-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mussymbol-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mussymbol-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/nonmusic-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/nonmusic-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/pic-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/pic-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/placeholder-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/placeholder-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/recsound-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/recsound-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/software-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/software-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/threed-icon.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/threed-icon.png
   branches/staff-client-experiment/Open-ILS/web/opac/skin/craftsman/
   branches/staff-client-experiment/Open-ILS/web/opac/theme/craftsman/
   branches/staff-client-experiment/Open-ILS/web/templates/default/actor/
   branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/acq/
   branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/action_trigger/
   branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/config/idl_field_doc.tt2
Removed:
   branches/staff-client-experiment/Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js
   branches/staff-client-experiment/Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2
Modified:
   branches/staff-client-experiment/
   branches/staff-client-experiment/Open-ILS/examples/fm_IDL.xml
   branches/staff-client-experiment/Open-ILS/examples/opensrf.xml.example
   branches/staff-client-experiment/Open-ILS/examples/opensrf_core.xml.example
   branches/staff-client-experiment/Open-ILS/src/Makefile.am
   branches/staff-client-experiment/Open-ILS/src/c-apps/Makefile.am
   branches/staff-client-experiment/Open-ILS/src/c-apps/dump_idl.c
   branches/staff-client-experiment/Open-ILS/src/c-apps/oils_cstore.c
   branches/staff-client-experiment/Open-ILS/src/c-apps/oils_idl-core.c
   branches/staff-client-experiment/Open-ILS/src/extras/fast-extract
   branches/staff-client-experiment/Open-ILS/src/extras/ils_events.xml
   branches/staff-client-experiment/Open-ILS/src/extras/org_tree_html_options.pl
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm
   branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/002.schema.config.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/005.schema.actors.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/040.schema.asset.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/070.schema.container.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/080.schema.money.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/090.schema.action.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/200.schema.acq.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/210.schema.serials.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/500.view.cross-schema.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/950.data.seed-values.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/999.functions.global.sql
   branches/staff-client-experiment/Open-ILS/src/sql/Pg/extend-reporter.sql
   branches/staff-client-experiment/Open-ILS/tests/datasets/README
   branches/staff-client-experiment/Open-ILS/web/conify/global/admin.js
   branches/staff-client-experiment/Open-ILS/web/css/skin/default.css
   branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/IDL.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/OrgUtils.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/User.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/Util.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/acq/CurrencyType.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/editors.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditDialog.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditPane.js
   branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/FilteringTreeSelect.js
   branches/staff-client-experiment/Open-ILS/web/opac/common/js/RemoteRequest.js
   branches/staff-client-experiment/Open-ILS/web/opac/images/progressbar_green.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/slimtree/folder2.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/slimtree/folderopen2.gif
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/cartographic.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mixed material.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/moving image.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/notated music.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/software, multimedia.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound recording-musical.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound recording-nonmusical.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound recording.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/still images.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/text.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/images/tor/three dimensional object.jpg
   branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/holds.js
   branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/myopac.js
   branches/staff-client-experiment/Open-ILS/web/opac/skin/default/xml/common/js_common.xml
   branches/staff-client-experiment/Open-ILS/web/templates/base.tt2
   branches/staff-client-experiment/Open-ILS/web/templates/default/menu.tt2
   branches/staff-client-experiment/Open-ILS/xul/staff_client/chrome/content/main/about.html
   branches/staff-client-experiment/Open-ILS/xul/staff_client/server/cat/marcedit.js
   branches/staff-client-experiment/Open-ILS/xul/staff_client/server/patron/ue_config.js
Log:
Merged revisions 12025,12028,12035,12037-12050,12053-12057,12060-12065,12067-12072,12074,12077-12100,12103-12114,12117-12125,12127,12131-12134,12137-12147,12149-12154,12156-12162,12164-12174,12177-12187,12189-12197,12200-12211,12213-12218,12223,12227 via svnmerge from 
svn://svn.open-ils.org/ILS/trunk

........
  r12025 | miker | 2009-01-31 17:04:33 -0500 (Sat, 31 Jan 2009) | 1 line
  
  non-grouped event firing, event locating and grouping
........
  r12028 | miker | 2009-01-31 23:42:59 -0500 (Sat, 31 Jan 2009) | 1 line
  
  adding event group support
........
  r12035 | scottmk | 2009-02-01 14:03:30 -0500 (Sun, 01 Feb 2009) | 8 lines
  
  Rearranged a bit for clarity.
  
  1. In doRetrieve: don't declare obj until we're ready to build it.
  
  2. In doFieldmapperSearch(): reversed an "if" test to clarify what's
  an early return and what's mainline logic.  Also: don't declare or
  create res_list until we know we're going to return it.
........
  r12037 | scottmk | 2009-02-01 16:26:36 -0500 (Sun, 01 Feb 2009) | 3 lines
  
  Replaced all occurrences of the macro OSRF_METHOD_VERIFY_CONTEXT
  with equivalent calls to the function osrfMethodVerifyContext().
........
  r12038 | miker | 2009-02-01 16:34:34 -0500 (Sun, 01 Feb 2009) | 1 line
  
  need the full hook, not just idlist
........
  r12039 | miker | 2009-02-01 16:35:44 -0500 (Sun, 01 Feb 2009) | 1 line
  
  keys is more efficient than map, since we already have the id list as the hash keys
........
  r12040 | dbs | 2009-02-02 01:53:55 -0500 (Mon, 02 Feb 2009) | 7 lines
  
  Add 98 Nepali MARC records to the test datasets
  
  On a stock OpenSRF 1.0.3 / Evergreen 1.4.0.1 machine, only 20 of these
  records are successfully added using the standard marc2bre / direct_ingest /
  pg_loader toolchain. Looks like errors decoding JSON containing exotic
  characters; also some strange fingerprints.
........
  r12041 | dbs | 2009-02-02 02:02:04 -0500 (Mon, 02 Feb 2009) | 2 lines
  
  It's important to mark this as application/octet-stream
........
  r12042 | dbs | 2009-02-02 15:12:10 -0500 (Mon, 02 Feb 2009) | 2 lines
  
  This kind of typo might cause headaches
........
  r12043 | erickson | 2009-02-03 11:26:58 -0500 (Tue, 03 Feb 2009) | 1 line
  
  added a cascading survey delete operation.
........
  r12044 | dbs | 2009-02-03 11:42:14 -0500 (Tue, 03 Feb 2009) | 1 line
  
  Initial attempt to add acquisitions to default configuration
........
  r12045 | erickson | 2009-02-03 12:33:51 -0500 (Tue, 03 Feb 2009) | 1 line
  
  fixed streaming vs. non-streaming logic error in currency_type retrieve.   took opportunity to port to pcrud
........
  r12046 | scottmk | 2009-02-03 12:53:48 -0500 (Tue, 03 Feb 2009) | 5 lines
  
  Added a couple of sanity checks to SELECT().  Return NULL if
  (1) the input jsonObject for the join tree is neither a hash, 
  nor an array, nor a string; or (2) it's a hash with more than
  one element.
........
  r12047 | dbs | 2009-02-03 13:12:43 -0500 (Tue, 03 Feb 2009) | 1 line
  
  Make pcrud a publicly available service, just like permacrud
........
  r12048 | erickson | 2009-02-03 14:24:16 -0500 (Tue, 03 Feb 2009) | 1 line
  
  added pcrud entries for exchange_rate
........
  r12049 | dbs | 2009-02-03 15:49:54 -0500 (Tue, 03 Feb 2009) | 3 lines
  
  Dojo wants locales passed to it in xx-yy format, not xx-YY.
  Closes #39.
........
  r12050 | dbs | 2009-02-03 15:54:03 -0500 (Tue, 03 Feb 2009) | 3 lines
  
  Dojo wants locales in xx-yy format, not xx-YY.
  Avoids #39 + 1.
........
  r12053 | scottmk | 2009-02-03 17:44:48 -0500 (Tue, 03 Feb 2009) | 13 lines
  
  Minor tweaks to the SELECT function:
  
  1. Narrowed the scope of the idlClass variable.
  
  2. Made cname const,
  
  3. In the loop for building the column list: eliminated an IDL lookup
  for the class name, because we already have it.  The IDL stores it
  redundantly in two different places, and we don't need to find both.
  
  4. Renamed __column and __alias to _column and _alias.  Identifiers
  beginning with two underscores are reserved.
........
  r12054 | scottmk | 2009-02-03 20:43:25 -0500 (Tue, 03 Feb 2009) | 7 lines
  
  This update tweaks the SELECT function, at the point commented
  "make sure the target relation is in the join tree".
  
  The new logic should be equivalent to the old, except that
  the old code potentially invokes undefined behavior by
  reading through a null pointer.
........
  r12055 | dbs | 2009-02-03 23:37:46 -0500 (Tue, 03 Feb 2009) | 2 lines
  
  And now make old-school gateway requests behave in the OPAC.
........
  r12056 | erickson | 2009-02-03 23:46:55 -0500 (Tue, 03 Feb 2009) | 1 line
  
  allow caller to pass additional onpostapply and oncancel handlers
........
  r12057 | erickson | 2009-02-03 23:47:38 -0500 (Tue, 03 Feb 2009) | 1 line
  
  better handle case where grid structure may exist but just have no columns yet defined
........
  r12060 | erickson | 2009-02-03 23:49:23 -0500 (Tue, 03 Feb 2009) | 1 line
  
  added some rough exchange rate editing code using auto widgets.  essentially functions, but more later
........
  r12061 | erickson | 2009-02-04 10:41:15 -0500 (Wed, 04 Feb 2009) | 1 line
  
  added pending column to represent pending approval from staff.
........
  r12062 | erickson | 2009-02-04 11:12:52 -0500 (Wed, 04 Feb 2009) | 1 line
  
  added pcrud as controller for asva
........
  r12063 | erickson | 2009-02-04 11:43:03 -0500 (Wed, 04 Feb 2009) | 1 line
  
  auto-grid now has the ability to pop up an edit dialog for the given fieldmapper object
........
  r12064 | scottmk | 2009-02-04 11:48:49 -0500 (Wed, 04 Feb 2009) | 4 lines
  
  In SELECT(): simplify and clarify the logic for building a default
  select list.  Also, for a minor performance boost: don't look up the
  core fields unless we're actually going to use them.
........
  r12065 | erickson | 2009-02-04 13:41:44 -0500 (Wed, 04 Feb 2009) | 1 line
  
  use built-in edit dialog
........
  r12067 | erickson | 2009-02-04 16:27:44 -0500 (Wed, 04 Feb 2009) | 1 line
  
  rely on the stored grid data to reconstitute a fm object for building the edit dialog
........
  r12068 | erickson | 2009-02-04 16:28:53 -0500 (Wed, 04 Feb 2009) | 1 line
  
  fixed bug where subsequent dialogs were appending to the the class-level fieldList array.  can now hit Enter to save instead of having to click Save
........
  r12069 | scottmk | 2009-02-04 16:49:06 -0500 (Wed, 04 Feb 2009) | 7 lines
  
  In SELECT(): further rewrote, and festooned with comments, the code
  that verifies that every column in the SELECT clause comes from a
  class in the FROM clause.
  
  Also, for readability: reversed an "if" test that treats functions
  differently from classes.
........
  r12070 | erickson | 2009-02-04 17:50:23 -0500 (Wed, 04 Feb 2009) | 1 line
  
  when using the popup dialog, overide the default focus handling.  this is still experimental
........
  r12071 | erickson | 2009-02-04 17:51:11 -0500 (Wed, 04 Feb 2009) | 1 line
  
  no need to import 2 grid css's, nor the private _grid css
........
  r12072 | erickson | 2009-02-04 23:36:08 -0500 (Wed, 04 Feb 2009) | 1 line
  
  selected-ness follow mouse and key navigation in a uniform fashion.
........
  r12074 | erickson | 2009-02-04 23:48:35 -0500 (Wed, 04 Feb 2009) | 1 line
  
  cleaned up to match latest autogrid
........
  r12077 | erickson | 2009-02-05 09:36:42 -0500 (Thu, 05 Feb 2009) | 1 line
  
  added option to launch creation dialog from auto grid.  passing final pcrud response to onPostSubmit handler
........
  r12078 | erickson | 2009-02-05 11:03:25 -0500 (Thu, 05 Feb 2009) | 1 line
  
  give dojo a td and it will replace it with the dijit node, so give dojo a sub-node to clobber instead
........
  r12079 | erickson | 2009-02-05 11:04:36 -0500 (Thu, 05 Feb 2009) | 1 line
  
  start of single label + action bar to sit above the grid
........
  r12080 | erickson | 2009-02-05 11:21:43 -0500 (Thu, 05 Feb 2009) | 1 line
  
  allow for definition of default cell width.  (can still be overridden by defining cell widths in the grid)
........
  r12081 | scottmk | 2009-02-05 11:35:44 -0500 (Thu, 05 Feb 2009) | 18 lines
  
  Various tweaks, mainly to the SELECT function.
  
  1. Moved some IDL lookups out of a loop where their results were loop invariants.
  
  2. Narrowed the scope of _alias.
  
  3. Eliminated some calls to jsonObjectToSimpleString() and strdup(), along with
  the associated mallocs and frees.
  
  4. Eliminated a couple of needlessly repeated calls to jsonObjectGetKey() by
  caching the results of the first ones.
  
  5. Uncommented a couple of commented-out calls to jsonIteratorFree(), because
  I don't see anything wrong with them.
  
  6. Moved another commented-out call back into a scope where it would compile
  if uncommented (but left it commented out for now).
........
  r12082 | erickson | 2009-02-05 12:52:24 -0500 (Thu, 05 Feb 2009) | 1 line
  
  more style tidying
........
  r12083 | erickson | 2009-02-05 12:53:05 -0500 (Thu, 05 Feb 2009) | 1 line
  
  consistent focus handling after user clicks cancel/save in edit dialog.  still funky when same actions are done from the create dialog
........
  r12084 | phasefx | 2009-02-05 13:25:39 -0500 (Thu, 05 Feb 2009) | 1 line
  
  output the deleted flag from the bre
........
  r12085 | erickson | 2009-02-05 17:00:41 -0500 (Thu, 05 Feb 2009) | 1 line
  
  added selector attr to currency type
........
  r12086 | erickson | 2009-02-05 18:10:29 -0500 (Thu, 05 Feb 2009) | 1 line
  
  repaired old pylons-ism with TT context locale.  note, the locale will need some cleaning-up in EGWeb.pm to be wholly functional
........
  r12087 | erickson | 2009-02-05 19:08:37 -0500 (Thu, 05 Feb 2009) | 1 line
  
  added support for auto-building select options from has_a links, using the selector attribute when defined.  mostly functional, needs some tweaking
........
  r12088 | miker | 2009-02-05 21:07:42 -0500 (Thu, 05 Feb 2009) | 1 line
  
  adding hold-cancel cause and note table and fields; teaching hold targeter how to use the cancel_cause field
........
  r12089 | scottmk | 2009-02-06 04:32:44 -0500 (Fri, 06 Feb 2009) | 11 lines
  
  Miscellaneous tweaks:
  
  1. In searchFieldTransform(): simplified the way we append subcolumns, to
  reduce the churning of memory.
  
  2. I found it confusing that we were using _column for two different
  (though similar) things.  So I split it into two separate variables in two
  separate scopes: col_name and _column.
  
  3. Don't bother looking up "i18n" if it's disabled anyway.
........
  r12090 | miker | 2009-02-06 09:28:30 -0500 (Fri, 06 Feb 2009) | 1 line
  
  add patron-opac cause, and comments about IDs
........
  r12091 | miker | 2009-02-06 10:05:41 -0500 (Fri, 06 Feb 2009) | 1 line
  
  teaching everyone how to note the pkey delete restriction policy for specific classes
........
  r12092 | miker | 2009-02-06 10:24:02 -0500 (Fri, 06 Feb 2009) | 1 line
  
  /actually/ pin the ids, and set the sequence appropriately
........
  r12093 | erickson | 2009-02-06 10:42:12 -0500 (Fri, 06 Feb 2009) | 1 line
  
  fixed problem with async widget building and saving (by using my own code as it was meant to be used)
........
  r12094 | erickson | 2009-02-06 10:52:21 -0500 (Fri, 06 Feb 2009) | 1 line
  
  added support for passing in cancellation cause and note
........
  r12095 | erickson | 2009-02-06 10:55:06 -0500 (Fri, 06 Feb 2009) | 1 line
  
  passing hold cancellation reason from opac-cancelled holds
........
  r12096 | erickson | 2009-02-06 11:15:14 -0500 (Fri, 06 Feb 2009) | 1 line
  
  added support for displaying the last X cancelled holds, most recently cancelled holds first
........
  r12097 | erickson | 2009-02-06 12:52:57 -0500 (Fri, 06 Feb 2009) | 1 line
  
  new method for un-canceling holds.  request_time and expire_time are reset if appropriate org-unit-setting is enabled
........
  r12098 | miker | 2009-02-06 12:58:04 -0500 (Fri, 06 Feb 2009) | 1 line
  
  use method_lookup instead of direct call ($self is not what you think...)
........
  r12099 | erickson | 2009-02-06 17:11:32 -0500 (Fri, 06 Feb 2009) | 1 line
  
  don't attempt to retrieve the linked selector objects if the data is not retrievable via pcrud
........
  r12100 | dbs | 2009-02-07 14:01:44 -0500 (Sat, 07 Feb 2009) | 1 line
  
  Make all FKs deferrable again
........
  r12103 | scottmk | 2009-02-07 19:28:13 -0500 (Sat, 07 Feb 2009) | 16 lines
  
  More tweaks, mostly to SELECT():
  
  1. When building the select list, insert a separator comma in one
  place instead of duplicating the code.
  
  2. Narrowed the scope of fname.
  
  3. Created a new transform_str variable instead of reusing _column
  for a different purpose.
  
  4. Juggled _column and _alias a bit so as to eliminate a strdup()
  and a free().
  
  5.In searchFieldTransform(): plugged a memory leak in the event of
  an error return. 
........
  r12104 | miker | 2009-02-07 21:12:31 -0500 (Sat, 07 Feb 2009) | 1 line
  
  adding note tables for all bucket and item tables; adding pos field for optional ordering of items within a bucket
........
  r12105 | miker | 2009-02-07 21:33:03 -0500 (Sat, 07 Feb 2009) | 1 line
  
  adding IDL for note tables for all bucket and item tables; adding IDL for pos field for optional ordering of items within a bucket
........
  r12106 | miker | 2009-02-07 21:37:56 -0500 (Sat, 07 Feb 2009) | 1 line
  
  add a record bucket type for reading lists
........
  r12107 | erickson | 2009-02-08 09:43:43 -0500 (Sun, 08 Feb 2009) | 1 line
  
  more provider data.  address, contact, and contact address
........
  r12108 | erickson | 2009-02-08 09:59:49 -0500 (Sun, 08 Feb 2009) | 1 line
  
  no need to publish provider contacts in reporter-store
........
  r12109 | erickson | 2009-02-08 11:05:20 -0500 (Sun, 08 Feb 2009) | 1 line
  
  autogrid can now fetch and load all data of a given class
........
  r12110 | erickson | 2009-02-08 11:06:36 -0500 (Sun, 08 Feb 2009) | 1 line
  
  use new built-in autogrid fetch & load
........
  r12111 | erickson | 2009-02-08 20:05:05 -0500 (Sun, 08 Feb 2009) | 1 line
  
  init datalist at startup so each successive instance isn't appending to the same array
........
  r12112 | miker | 2009-02-08 22:34:12 -0500 (Sun, 08 Feb 2009) | 1 line
  
  IE does not yet support Array.forEach ... so we fake it with dojo
........
  r12113 | miker | 2009-02-08 22:34:48 -0500 (Sun, 08 Feb 2009) | 1 line
  
  replace the (very slow) view with the materialized version
........
  r12114 | erickson | 2009-02-09 09:12:41 -0500 (Mon, 09 Feb 2009) | 1 line
  
  function to wrap up a xact-base storage request in 1 call
........
  r12117 | erickson | 2009-02-09 12:56:19 -0500 (Mon, 09 Feb 2009) | 1 line
  
  teach the IDL about the existence of pkey sequences
........
  r12118 | erickson | 2009-02-09 12:56:49 -0500 (Mon, 09 Feb 2009) | 1 line
  
  not sure why views[1] was working before, but it's not now
........
  r12119 | erickson | 2009-02-09 13:09:43 -0500 (Mon, 09 Feb 2009) | 1 line
  
  removed some unnecessary console logs
........
  r12120 | erickson | 2009-02-09 13:47:25 -0500 (Mon, 09 Feb 2009) | 1 line
  
  can now specify post update and create handlers.  wrap re-focus in try
........
  r12121 | erickson | 2009-02-09 13:51:24 -0500 (Mon, 09 Feb 2009) | 1 line
  
  moved currency type config into conify, using autogrid
........
  r12122 | erickson | 2009-02-09 16:53:48 -0500 (Mon, 09 Feb 2009) | 1 line
  
  created read-only version of a single widget and edit pane
........
  r12123 | erickson | 2009-02-09 16:54:27 -0500 (Mon, 09 Feb 2009) | 1 line
  
  by default, don't show sequence columns, which will usually be 'id' columns.  configurable
........
  r12124 | djfiander | 2009-02-09 21:52:54 -0500 (Mon, 09 Feb 2009) | 1 line
  
  A bunch of untested code to support serials predictions
........
  r12125 | scottmk | 2009-02-09 22:46:33 -0500 (Mon, 09 Feb 2009) | 15 lines
  
  Miscellaneous tweaks:
  
  1. Simplified the logic in searchValueTransform().
  
  2. In a debug message: changed format specification to %p for pointer
  values, instead of %d.  Using %d will garble the output if ints and
  pointers don't have the same size (and they don't on my laptop).
  
  3. Corrected a typo in another message. The word "non-existant"
  is non-existent.
  
  4. For clarity: in several places, reversed the logic of some
  if/elses so that we test for a positive condition instead of 
  a negative condition.
........
  r12127 | erickson | 2009-02-10 12:05:53 -0500 (Tue, 10 Feb 2009) | 1 line
  
  sort bucket items by 'pos' field
........
  r12131 | erickson | 2009-02-10 13:17:32 -0500 (Tue, 10 Feb 2009) | 1 line
  
  forward port craftsman skin : svn merge -r12127:12130 svn://svn.open-ils.org/ILS/branches/rel_1_4
........
  r12132 | erickson | 2009-02-10 14:06:53 -0500 (Tue, 10 Feb 2009) | 1 line
  
  flesh item notes on retrieval.  refactor retrieval to use cstoreditor
........
  r12133 | erickson | 2009-02-10 14:08:51 -0500 (Tue, 10 Feb 2009) | 1 line
  
  added bucket item notes virtual fields
........
  r12134 | dbs | 2009-02-10 14:16:33 -0500 (Tue, 10 Feb 2009) | 2 lines
  
  Get the real org_unit_type depth from aout
........
  r12137 | erickson | 2009-02-10 14:41:10 -0500 (Tue, 10 Feb 2009) | 1 line
  
  handle item notes on cascade delete.  created item note CUD method.  more cstoreditor-ification
........
  r12138 | erickson | 2009-02-10 15:46:08 -0500 (Tue, 10 Feb 2009) | 1 line
  
  handle case where an address is pending but replaces no other address
........
  r12139 | erickson | 2009-02-10 15:46:31 -0500 (Tue, 10 Feb 2009) | 1 line
  
  entry for usr address pending flag
........
  r12140 | erickson | 2009-02-10 15:46:58 -0500 (Tue, 10 Feb 2009) | 1 line
  
  set pending true on altered addresses
........
  r12141 | erickson | 2009-02-10 15:49:11 -0500 (Tue, 10 Feb 2009) | 1 line
  
  return addr id for consistency
........
  r12142 | erickson | 2009-02-10 15:51:55 -0500 (Tue, 10 Feb 2009) | 1 line
  
  allow approval of pending address, even if it does not replace any other address
........
  r12143 | erickson | 2009-02-10 16:24:39 -0500 (Tue, 10 Feb 2009) | 1 line
  
  take advantage of the new billable_transaction_summary table.  also, force caller to make_mbts to pass an editor in
........
  r12144 | erickson | 2009-02-10 17:44:51 -0500 (Tue, 10 Feb 2009) | 1 line
  
  initial support for adding entries to a patron's items-checked-out list
........
  r12145 | scottmk | 2009-02-10 23:12:08 -0500 (Tue, 10 Feb 2009) | 9 lines
  
  1. Applied a uniform standard to determine whether a string represents
  true or false.  A few remaining spots are unchanged for now because
  they are dubious on other grounds.
  
  2. In one spot: replaced jsonParseString("true") with jsonNewBoolObject( 1 ).
  
  3. In oilsMakeFieldmapperFromResult(): broke up a confusing "if" test
  into two simpler ones.
........
  r12146 | dbs | 2009-02-10 23:15:19 -0500 (Tue, 10 Feb 2009) | 2 lines
  
  Doh! slight HTML problem
........
  r12147 | erickson | 2009-02-10 23:33:16 -0500 (Tue, 10 Feb 2009) | 1 line
  
  plugged in checkout history bucket population to checkout process
........
  r12149 | miker | 2009-02-11 12:02:19 -0500 (Wed, 11 Feb 2009) | 1 line
  
  fix off-by-one on function literal construction (for function-in-from and literal value transforms); use the function name as the from clause alias, avoiding "(null)" as the alias
........
  r12150 | scottmk | 2009-02-11 13:55:11 -0500 (Wed, 11 Feb 2009) | 7 lines
  
  1. Corrected the enforcement of the readonly attribute for classes.
  It was backwards, but harmlessly so because the enforcement is
  redundant.
  
  2. Reversed the default for the "virtual" attribute of a field.
  Formerly it defaulted to true; now it defaults to false.
........
  r12151 | erickson | 2009-02-11 15:43:00 -0500 (Wed, 11 Feb 2009) | 1 line
  
  use copy buckets instead (for precats) and verify the copy bucket mechanism is globally configured
........
  r12152 | erickson | 2009-02-11 16:18:28 -0500 (Wed, 11 Feb 2009) | 1 line
  
  fixed broken array ref handling
........
  r12153 | miker | 2009-02-11 16:31:05 -0500 (Wed, 11 Feb 2009) | 1 line
  
  add idl field documenation table and accompanying IDL entry ... crazy recursive docs
........
  r12154 | miker | 2009-02-11 16:33:10 -0500 (Wed, 11 Feb 2009) | 1 line
  
  no, we want that, really
........
  r12156 | miker | 2009-02-12 01:31:32 -0500 (Thu, 12 Feb 2009) | 1 line
  
  adding IN subquery support
........
  r12157 | erickson | 2009-02-12 11:33:20 -0500 (Thu, 12 Feb 2009) | 1 line
  
  fixed wiget value accessor/mutator which resulted in values with embedded wiget objects (resulting in infinite recursion in js2JSON)
........
  r12158 | erickson | 2009-02-12 12:01:20 -0500 (Thu, 12 Feb 2009) | 1 line
  
  new provider ui, based on autogrid and moved into conify (editing static data).  leaving other in place until all functionality is ported over
........
  r12159 | erickson | 2009-02-12 12:01:53 -0500 (Thu, 12 Feb 2009) | 1 line
  
  slight reformatting for readability
........
  r12160 | erickson | 2009-02-12 12:25:40 -0500 (Thu, 12 Feb 2009) | 1 line
  
  added pcrud entries for trigger event def and hook
........
  r12161 | dbs | 2009-02-12 12:27:04 -0500 (Thu, 12 Feb 2009) | 2 lines
  
  Revert r12071 until we port jubgrid.js over to the Dojo 1.2 DataGrid
........
  r12162 | scottmk | 2009-02-12 14:25:37 -0500 (Thu, 12 Feb 2009) | 2 lines
  
  Pass down an osrfMethodContext* where we need one.
........
  r12164 | dbs | 2009-02-12 15:15:35 -0500 (Thu, 12 Feb 2009) | 2 lines
  
  Further testing suggests that dojox/resources/Grid.css is not needed, and in fact causes problems
........
  r12165 | erickson | 2009-02-12 17:12:54 -0500 (Thu, 12 Feb 2009) | 1 line
  
  pcrud entries for action_trigger validator and reactor
........
  r12166 | erickson | 2009-02-12 17:22:22 -0500 (Thu, 12 Feb 2009) | 1 line
  
  repaired fkey column names
........
  r12167 | erickson | 2009-02-12 17:37:01 -0500 (Thu, 12 Feb 2009) | 1 line
  
  no need for a store if there is no tree data (don't attempt to access dataList[0])
........
  r12168 | dbs | 2009-02-13 00:15:26 -0500 (Fri, 13 Feb 2009) | 2 lines
  
  Add VIEW_BILLING_TYPE permission to Staff
........
  r12169 | erickson | 2009-02-13 09:19:03 -0500 (Fri, 13 Feb 2009) | 1 line
  
  pcrud for action_trigger.cleanup
........
  r12170 | erickson | 2009-02-13 11:14:40 -0500 (Fri, 13 Feb 2009) | 1 line
  
  func comments, treat "" as null
........
  r12171 | erickson | 2009-02-13 11:15:14 -0500 (Fri, 13 Feb 2009) | 1 line
  
  don't display auto-generated fields in create dialog
........
  r12172 | erickson | 2009-02-13 11:15:58 -0500 (Fri, 13 Feb 2009) | 1 line
  
  when no field order is defined but fields existin in the markup, start there with the order
........
  r12173 | erickson | 2009-02-13 11:17:21 -0500 (Fri, 13 Feb 2009) | 1 line
  
  UI for managing trigger event_defs, hooks, reactors, and validators -- all auto-generated at this point, but will need some additions
........
  r12174 | dbs | 2009-02-13 12:03:05 -0500 (Fri, 13 Feb 2009) | 3 lines
  
  Give the Administrator user an explicit work_ou of 1 (Consortium)
  Fixes calls to open-ils.actor.user.work_perm.highest_org_set.batch and friends that returned NULL for Administrator user
........
  r12177 | erickson | 2009-02-13 14:58:24 -0500 (Fri, 13 Feb 2009) | 1 line
  
  fixed fkey col name
........
  r12178 | erickson | 2009-02-13 15:08:30 -0500 (Fri, 13 Feb 2009) | 1 line
  
  fixed org unit data type
........
  r12179 | erickson | 2009-02-13 17:10:08 -0500 (Fri, 13 Feb 2009) | 1 line
  
  can now override any given auto-widget with a locally defined widget
........
  r12180 | erickson | 2009-02-13 17:21:34 -0500 (Fri, 13 Feb 2009) | 1 line
  
  UI for manging idl_field_doc entries
........
  r12181 | miker | 2009-02-13 20:11:31 -0500 (Fri, 13 Feb 2009) | 1 line
  
  cannot use "classname", we have reserved that in the js, and perl uses class_name. so, we will use fm_class
........
  r12182 | erickson | 2009-02-14 12:28:50 -0500 (Sat, 14 Feb 2009) | 1 line
  
  updated to match new fm_class field name
........
  r12183 | scottmk | 2009-02-14 21:40:40 -0500 (Sat, 14 Feb 2009) | 6 lines
  
  1. in searchJOIN(): fixed a bug in a couple of loops that search for
  links in the IDL.  We were incrementing the index twice in each
  iteration, and thereby examining only ever other entry.
  
  2. Constified a couple of character pointers.
........
  r12184 | erickson | 2009-02-15 11:39:19 -0500 (Sun, 15 Feb 2009) | 1 line
  
  don't apply on-enter-equals-submit handler, cuz enter is conventiently used in various widgets to select values
........
  r12185 | erickson | 2009-02-15 11:41:22 -0500 (Sun, 15 Feb 2009) | 1 line
  
  added store reset func
........
  r12186 | erickson | 2009-02-15 11:58:57 -0500 (Sun, 15 Feb 2009) | 1 line
  
  linked in some more UI's
........
  r12187 | dbs | 2009-02-15 13:53:56 -0500 (Sun, 15 Feb 2009) | 2 lines
  
  Cast LOWER arguments explicitly to text to deal with LOWER(id) - throws an exception on PostgreSQL 8.3
........
  r12189 | phasefx | 2009-02-15 17:30:44 -0500 (Sun, 15 Feb 2009) | 1 line
  
  grammar-o.  Thanks Trinculo!
........
  r12190 | dbs | 2009-02-15 21:14:28 -0500 (Sun, 15 Feb 2009) | 1 line
  
  Enable local administrators to run / view / share reports by default
........
  r12191 | erickson | 2009-02-16 13:55:09 -0500 (Mon, 16 Feb 2009) | 1 line
  
  in addition to the field list, provide a name-based hash for faster/simpler runtime lookup
........
  r12192 | erickson | 2009-02-16 16:55:52 -0500 (Mon, 16 Feb 2009) | 1 line
  
  plugged in perm group tree builder
........
  r12193 | erickson | 2009-02-16 17:00:06 -0500 (Mon, 16 Feb 2009) | 1 line
  
  experimental flattened/idl-aware user editor that takes advantage of auto-widgets and field-docs (i.e. context help)
........
  r12194 | erickson | 2009-02-16 21:30:46 -0500 (Mon, 16 Feb 2009) | 1 line
  
  initial plugin of staff-cats
........
  r12195 | erickson | 2009-02-16 22:34:13 -0500 (Mon, 16 Feb 2009) | 1 line
  
  stat cat entries plugged in
........
  r12196 | erickson | 2009-02-16 23:10:06 -0500 (Mon, 16 Feb 2009) | 1 line
  
  plugged in surveys
........
  r12197 | dbs | 2009-02-16 23:59:13 -0500 (Mon, 16 Feb 2009) | 1 line
  
  en-US is better than no MARC editor tooltips at all; but we really need to HEAD the requested locale
........
  r12200 | scottmk | 2009-02-17 09:03:35 -0500 (Tue, 17 Feb 2009) | 10 lines
  
  Opened a window into the innards of oils_cstore.c.
  
  1. Gave external linkage to the SELECT function.
  
  2. Added a new test_json_query.c, which calls the newly extern
  SELECT() to translate a JSON query into SQL.  To be used for
  testing, debugging, and troubleshooting.  Note that this
  utility calls SELECT() by normal linkage, not by dynamic
  loading.
........
  r12201 | erickson | 2009-02-17 09:20:07 -0500 (Tue, 17 Feb 2009) | 1 line
  
  use colspan 0 to span the table length
........
  r12202 | erickson | 2009-02-17 10:40:53 -0500 (Tue, 17 Feb 2009) | 1 line
  
  pcrud entries for ident_type
........
  r12203 | erickson | 2009-02-17 11:12:46 -0500 (Tue, 17 Feb 2009) | 1 line
  
  plugged in saving of new users
........
  r12204 | dbs | 2009-02-17 11:42:10 -0500 (Tue, 17 Feb 2009) | 2 lines
  
  Let's build test_json_query as part of autotools
........
  r12205 | erickson | 2009-02-17 13:06:13 -0500 (Tue, 17 Feb 2009) | 1 line
  
  repaired org setting calls.  added static version so no org-unit reference is required
........
  r12206 | erickson | 2009-02-17 13:06:36 -0500 (Tue, 17 Feb 2009) | 1 line
  
  added batch org setting method
........
  r12207 | erickson | 2009-02-17 13:06:57 -0500 (Tue, 17 Feb 2009) | 1 line
  
  explicitly load Util
........
  r12208 | erickson | 2009-02-17 17:49:37 -0500 (Tue, 17 Feb 2009) | 1 line
  
  added int/float widgets
........
  r12209 | erickson | 2009-02-17 17:50:41 -0500 (Tue, 17 Feb 2009) | 1 line
  
  plugged in new/delete addr handling
........
  r12210 | erickson | 2009-02-17 17:57:37 -0500 (Tue, 17 Feb 2009) | 1 line
  
  allow for the setting of the page title from the sub-template
........
  r12211 | scottmk | 2009-02-17 18:19:16 -0500 (Tue, 17 Feb 2009) | 3 lines
  
  In searchJOIN(): added a bit of sanity checking, to prevent
  segfaults induced by certain kinds of malformed JSOn queries.
........
  r12213 | erickson | 2009-02-17 23:48:34 -0500 (Tue, 17 Feb 2009) | 1 line
  
  dojo-based interval2seconds
........
  r12214 | erickson | 2009-02-17 23:49:20 -0500 (Tue, 17 Feb 2009) | 1 line
  
  added onchange to set expire date based on profile group selected
........
  r12215 | erickson | 2009-02-17 23:52:25 -0500 (Tue, 17 Feb 2009) | 1 line
  
  initial dojo-ified xul glue
........
  r12216 | dbs | 2009-02-17 23:55:26 -0500 (Tue, 17 Feb 2009) | 3 lines
  
  Avoid syntax errors due to mix of multi-valued INSERT clauses and single-valued INSERT clauses
  Avoid duplicate key error in that pesky permissions list
........
  r12217 | scottmk | 2009-02-18 02:51:39 -0500 (Wed, 18 Feb 2009) | 4 lines
  
  searchJOIN() was segfaulting when the FROM clause was of
  the form "from": { "xxx":"yyy" }.  Reason: we were
  freeing freeable_hash prematurely.  Fixed.
........
  r12218 | erickson | 2009-02-18 12:15:13 -0500 (Wed, 18 Feb 2009) | 1 line
  
  added onchange to copy new barcodes into usrname field
........
  r12223 | scottmk | 2009-02-18 16:25:48 -0500 (Wed, 18 Feb 2009) | 3 lines
  
  Correct an error in the link specification for class asv.
  (Class aou doesn't have a "survey" field.)
........
  r12227 | scottmk | 2009-02-18 21:12:46 -0500 (Wed, 18 Feb 2009) | 14 lines
  
  In searchJOIN: substantially rewrote the code for the condition where
  the JSON query specifies neither of the join columns.
  
  1. Corrected an apparent bug whereby we could join in only one
  direction, even when a join in the opposite direction was
  valid and equivalent.
  
  2. Rewrote the search loops for better performance.
  
  3. When testing for the success of the searches for join columns,
  success should mean that we have identified both columns.  Identifying
  just one isn't enough.
........



Property changes on: branches/staff-client-experiment
___________________________________________________________________
Name: svnmerge-integrated
   - /trunk:1-12022
   + /trunk:1-12227

Modified: branches/staff-client-experiment/Open-ILS/examples/fm_IDL.xml
===================================================================
--- branches/staff-client-experiment/Open-ILS/examples/fm_IDL.xml	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/examples/fm_IDL.xml	2009-02-19 05:39:53 UTC (rev 12228)
@@ -580,7 +580,7 @@
         </links>
     </class>
 
-	<class id="ath" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::hook" oils_persist:tablename="action_trigger.hook" reporter:label="Trigger hook point">
+	<class id="ath" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action_trigger::hook" oils_persist:tablename="action_trigger.hook" reporter:label="Trigger hook point">
 		<fields oils_persist:primary="key">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -591,6 +591,14 @@
 			<field reporter:label="Passive" name="passive" oils_obj:array_position="6" oils_persist:virtual="false"  reporter:datatype="bool"/>
 		</fields>
 		<links/>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="ADMIN_TRIGGER_HOOK" global_required="true"/>
+				<retrieve/>
+				<update permission="ADMIN_TRIGGER_HOOK" global_required="true"/>
+				<delete permission="ADMIN_TRIGGER_HOOK" global_required="true"/>
+			</actions>
+		</permacrud>
 	</class>
 
 	<class id="atcol" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::collector" oils_persist:tablename="action_trigger.collector" reporter:label="Trigger Evironment Collector">
@@ -604,7 +612,7 @@
 		<links/>
 	</class>
 
-	<class id="atval" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::validator" oils_persist:tablename="action_trigger.validator" reporter:label="Trigger Condition Validator">
+	<class id="atval" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action_trigger::validator" oils_persist:tablename="action_trigger.validator" reporter:label="Trigger Condition Validator">
 		<fields oils_persist:primary="module">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -613,9 +621,17 @@
 			<field reporter:label="Description" name="description" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true"/>
 		</fields>
 		<links/>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="ADMIN_TRIGGER_VALIDATOR" global_required="true"/>
+				<retrieve/>
+				<update permission="ADMIN_TRIGGER_VALIDATOR" global_required="true"/>
+				<delete permission="ADMIN_TRIGGER_VALIDATOR" global_required="true"/>
+			</actions>
+		</permacrud>
 	</class>
 
-	<class id="atreact" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::reactor" oils_persist:tablename="action_trigger.reactor" reporter:label="Trigger Event Reactor">
+	<class id="atreact" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action_trigger::reactor" oils_persist:tablename="action_trigger.reactor" reporter:label="Trigger Event Reactor">
 		<fields oils_persist:primary="module">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -624,9 +640,17 @@
 			<field reporter:label="Description" name="description" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true"/>
 		</fields>
 		<links/>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="ADMIN_TRIGGER_REACTOR" global_required="true"/>
+				<retrieve/>
+				<update permission="ADMIN_TRIGGER_REACTOR" global_required="true"/>
+				<delete permission="ADMIN_TRIGGER_REACTOR" global_required="true"/>
+			</actions>
+		</permacrud>
 	</class>
 
-	<class id="atclean" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::cleanup" oils_persist:tablename="action_trigger.cleanup" reporter:label="Trigger Event Cleanup">
+	<class id="atclean" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action_trigger::cleanup" oils_persist:tablename="action_trigger.cleanup" reporter:label="Trigger Event Cleanup">
 		<fields oils_persist:primary="module">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -635,6 +659,14 @@
 			<field reporter:label="Description" name="description" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true"/>
 		</fields>
 		<links/>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="ADMIN_TRIGGER_CLEANUP" global_required="true"/>
+				<retrieve/>
+				<update permission="ADMIN_TRIGGER_CLEANUP" global_required="true"/>
+				<delete permission="ADMIN_TRIGGER_CLEANUP" global_required="true"/>
+			</actions>
+		</permacrud>
 	</class>
 
 	<class id="atenv" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::environment" oils_persist:tablename="action_trigger.environment" reporter:label="Trigger Event Environment Entry">
@@ -654,7 +686,7 @@
 		</links>
 	</class>
 
-	<class id="atevdef" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::event_definition" oils_persist:tablename="action_trigger.event_definition" reporter:label="Trigger Event Definition">
+	<class id="atevdef" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action_trigger::event_definition" oils_persist:tablename="action_trigger.event_definition" reporter:label="Trigger Event Definition">
 		<fields oils_persist:primary="id" oils_persist:sequence="action_trigger.event_definition_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -676,14 +708,22 @@
 		</fields>
 		<links>
 			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
-			<link field="hook" reltype="has_a" key="id" map="" class="ath"/>
-			<link field="validator" reltype="has_a" key="id" map="" class="atval"/>
-			<link field="reactor" reltype="has_a" key="id" map="" class="atreact"/>
-			<link field="cleanup_success" reltype="has_a" key="id" map="" class="atclean"/>
-			<link field="cleanup_failure" reltype="has_a" key="id" map="" class="atclean"/>
+			<link field="hook" reltype="has_a" key="key" map="" class="ath"/>
+			<link field="validator" reltype="has_a" key="module" map="" class="atval"/>
+			<link field="reactor" reltype="has_a" key="module" map="" class="atreact"/>
+			<link field="cleanup_success" reltype="has_a" key="module" map="" class="atclean"/>
+			<link field="cleanup_failure" reltype="has_a" key="module" map="" class="atclean"/>
 			<link field="env" reltype="has_many" key="id" map="" class="atenv"/>
 			<link field="params" reltype="has_many" key="id" map="" class="atevparam"/>
 		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="ADMIN_TRIGGER_EVENT_DEF" context_field="owner"/>
+				<retrieve permission="ADMIN_TRIGGER_EVENT_DEF" context_field="owner"/>
+				<update permission="ADMIN_TRIGGER_EVENT_DEF" context_field="owner"/>
+				<delete permission="ADMIN_TRIGGER_EVENT_DEF" context_field="owner"/>
+			</actions>
+		</permacrud>
 	</class>
 
 	<class id="atev" controller="open-ils.cstore" oils_obj:fieldmapper="action_trigger::event" oils_persist:tablename="action_trigger.event" reporter:label="Trigger Event Entry">
@@ -956,7 +996,7 @@
 		</links>
 	</class>
 
-	<class id="cit" controller="open-ils.cstore" oils_obj:fieldmapper="config::identification_type" oils_persist:tablename="config.identification_type" reporter:label="Identification Type">
+	<class id="cit" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::identification_type" oils_persist:tablename="config.identification_type" reporter:label="Identification Type">
 		<fields oils_persist:primary="id" oils_persist:sequence="config.identification_type_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -965,6 +1005,14 @@
 			<field reporter:label="Identification Name" name="name" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true"/>
 		</fields>
 		<links/>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_IDENT_TYPE" global_required="true"/>
+                <retrieve/>
+                <update permission="ADMIN_IDENT_TYPE" global_required="true"/>
+                <delete permission="ADMIN_IDENT_TYPE" global_required="true"/>
+            </actions>
+        </permacrud>
 	</class>
 
 	<class id="asvq" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::survey_question" oils_persist:tablename="action.survey_question" reporter:label="User Survey Question">
@@ -998,7 +1046,7 @@
             </actions>
         </permacrud>
 	</class>
-	<class id="mbts" controller="open-ils.cstore" oils_obj:fieldmapper="money::billable_transaction_summary" oils_persist:tablename="money.billable_xact_summary" reporter:label="Billable Transaction Summary">
+	<class id="mbts" controller="open-ils.cstore" oils_obj:fieldmapper="money::billable_transaction_summary" oils_persist:tablename="money.materialized_billable_xact_summary" reporter:label="Billable Transaction Summary" oils_persist:readonly="true">
 		<fields oils_persist:primary="id" oils_persist:sequence="">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -1181,12 +1229,28 @@
 			<field name="id" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="id" />
 			<field name="target_copy" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link"/>
 			<field name="create_time" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="timestamp" />
+			<field name="pos" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="int" />
+			<field name="notes" oils_obj:array_position="8" oils_persist:virtual="true" reporter:datatype="link" />
 		</fields>
 		<links>
 			<link field="target_copy" reltype="has_a" key="id" map="" class="acp"/>
 			<link field="bucket" reltype="has_a" key="id" map="" class="ccb"/>
+            <link field="notes" reltype="has_many" map="" key="item" class="ccbin"/>
 		</links>
 	</class>
+	<class id="ccbin" controller="open-ils.cstore" oils_obj:fieldmapper="container::copy_bucket_item_note" oils_persist:tablename="container.copy_bucket_item_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.copy_bucket_item_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="item" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="item" reltype="has_a" key="id" map="" class="ccbi"/>
+		</links>
+	</class>
 	<class id="are" controller="open-ils.cstore" oils_obj:fieldmapper="authority::record_entry" oils_persist:tablename="authority.record_entry">
 		<fields oils_persist:primary="id" oils_persist:sequence="authority.record_entry_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -1964,7 +2028,7 @@
             </actions>
         </permacrud>
 	</class>
-	<class id="ccs" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::copy_status" oils_persist:tablename="config.copy_status">
+	<class id="ccs" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::copy_status" oils_persist:tablename="config.copy_status" oils_persist:restrict_primary="100">
 		<fields oils_persist:primary="id" oils_persist:sequence="config.copy_status_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -1984,7 +2048,7 @@
             </actions>
         </permacrud>
 	</class>
-	<class id="ausp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::user_standing_penalty" oils_persist:tablename="actor.usr_standing_penalty">
+	<class id="ausp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::user_standing_penalty" oils_persist:tablename="actor.usr_standing_penalty" oils_persist:restrict_primary="100">
 		<fields oils_persist:primary="id" oils_persist:sequence="actor.usr_standing_penalty_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -2030,6 +2094,7 @@
 			<field reporter:label="Valid Address?" name="valid" oils_obj:array_position="13" oils_persist:virtual="false" reporter:datatype="bool"/>
 			<field reporter:label="Within City Limits?" name="within_city_limits" oils_obj:array_position="14" oils_persist:virtual="false" reporter:datatype="bool"/>
 			<field reporter:label="Replaces" name="replaces" oils_obj:array_position="15" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field reporter:label="Pending" name="pending" oils_obj:array_position="16" oils_persist:virtual="false" reporter:datatype="bool"/>
 		</fields>
 		<links>
 			<link field="usr" reltype="has_a" key="id" map="" class="au"/>
@@ -2292,12 +2357,28 @@
 			<field name="id" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="id" />
 			<field name="target_call_number" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link" />
 			<field name="create_time" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="timestamp" />
+			<field name="pos" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="int" />
+			<field name="notes" oils_obj:array_position="8" oils_persist:virtual="true" reporter:datatype="link" />
 		</fields>
 		<links>
 			<link field="target_call_number" reltype="has_a" key="id" map="" class="acn"/>
 			<link field="bucket" reltype="has_a" key="id" map="" class="ccnb"/>
+            <link field="notes" reltype="has_many" map="" key="item" class="ccnbin"/>
 		</links>
 	</class>
+	<class id="ccnbin" controller="open-ils.cstore" oils_obj:fieldmapper="container::call_number_bucket_item_note" oils_persist:tablename="container.call_number_bucket_item_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.call_number_bucket_item_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="item" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="item" reltype="has_a" key="id" map="" class="ccnbi"/>
+		</links>
+	</class>
 	<class id="cbreb" controller="open-ils.cstore" oils_obj:fieldmapper="container::biblio_record_entry_bucket" oils_persist:tablename="container.biblio_record_entry_bucket">
 		<fields oils_persist:primary="id" oils_persist:sequence="container.biblio_record_entry_bucket_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -2316,6 +2397,19 @@
 			<link field="items" reltype="has_many" key="bucket" map="" class="cbrebi"/>
 		</links>
 	</class>
+	<class id="cbrebn" controller="open-ils.cstore" oils_obj:fieldmapper="container::biblio_record_entry_bucket_note" oils_persist:tablename="container.biblio_record_entry_bucket_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.biblio_record_entry_bucket_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="bucket" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="bucket" reltype="has_a" key="id" map="" class="cbreb"/>
+		</links>
+	</class>
 	<class id="ahcm" controller="open-ils.cstore" oils_obj:fieldmapper="action::hold_copy_map" oils_persist:tablename="action.hold_copy_map">
 		<fields oils_persist:primary="id" oils_persist:sequence="action.hold_copy_map_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -2537,7 +2631,7 @@
 			<field reporter:label="Display in User Summary" name="usr_summary" oils_obj:array_position="14" oils_persist:virtual="false" reporter:datatype="bool"/>
 		</fields>
 		<links>
-			<link field="owner" reltype="has_a" key="survey" map="" class="aou"/>
+			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
 			<link field="responses" reltype="has_many" key="survey" map="" class="asvr"/>
 			<link field="questions" reltype="has_many" key="survey" map="" class="asvq"/>
 		</links>
@@ -2614,6 +2708,8 @@
 			<field reporter:label="Eligible Copies" name="eligible_copies" oils_obj:array_position="30" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Currently Frozen" name="frozen" oils_obj:array_position="31" oils_persist:virtual="false" reporter:datatype="bool"/>
 			<field reporter:label="Thaw Date (if frozen)" name="thaw_date" oils_obj:array_position="32" oils_persist:virtual="false" reporter:datatype="timestamp"/>
+			<field reporter:label="Cancelation cause" name="cancel_cause" oils_obj:array_position="33" oils_persist:virtual="false" reporter:datatype="link" />
+			<field reporter:label="Cancelation note" name="cancel_note" oils_obj:array_position="34" oils_persist:virtual="false" reporter:datatype="text" />
 		</fields>
 		<links>
 			<link field="fulfillment_lib" reltype="has_a" key="id" map="" class="aou"/>
@@ -2628,6 +2724,7 @@
 			<link field="notifications" reltype="has_many" key="hold" map="" class="ahn"/>
 			<link field="eligible_copies" reltype="has_many" key="hold" map="target_copy" class="ahcm"/>
 			<link field="bib_rec" reltype="might_have" key="id" map="" class="rhrr"/>
+			<link field="cancel_cause" reltype="might_have" key="id" map="" class="ahrcc"/>
 		</links>
 	</class>
 	<class id="aou" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::org_unit" oils_persist:tablename="actor.org_unit" reporter:label="Organizational Unit" oils_persist:field_safe="true">
@@ -2702,6 +2799,19 @@
 			<link field="items" reltype="has_many" key="bucket" map="" class="ccnbi"/>
 		</links>
 	</class>
+	<class id="ccnbn" controller="open-ils.cstore" oils_obj:fieldmapper="container::call_number_bucket_note" oils_persist:tablename="container.call_number_bucket_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.call_number_bucket_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="bucket" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="bucket" reltype="has_a" key="id" map="" class="ccnb"/>
+		</links>
+	</class>
 	<class id="asc" controller="open-ils.cstore" oils_obj:fieldmapper="asset::stat_cat" oils_persist:tablename="asset.stat_cat" reporter:label="Asset Statistical Category">
 		<fields oils_persist:primary="id" oils_persist:sequence="asset.stat_cat_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -2781,6 +2891,19 @@
 			<link field="items" reltype="has_many" key="bucket" map="" class="cubi"/>
 		</links>
 	</class>
+	<class id="cubn" controller="open-ils.cstore" oils_obj:fieldmapper="container::user_bucket_note" oils_persist:tablename="container.user_bucket_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.user_bucket_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="bucket" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="bucket" reltype="has_a" key="id" map="" class="cub"/>
+		</links>
+	</class>
 	<class id="mcrp" controller="open-ils.cstore" oils_obj:fieldmapper="money::credit_payment" oils_persist:tablename="money.credit_payment" reporter:label="House Credit Payment">
 		<fields oils_persist:primary="id" oils_persist:sequence="money.payment_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -2995,12 +3118,28 @@
 			<field name="id" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="id" />
 			<field name="target_user" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link"/>
 			<field name="create_time" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="timestamp" />
+			<field name="pos" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="int" />
+			<field name="notes" oils_obj:array_position="8" oils_persist:virtual="true" reporter:datatype="link" />
 		</fields>
 		<links>
 			<link field="target_user" reltype="has_a" key="id" map="" class="au"/>
 			<link field="bucket" reltype="has_a" key="id" map="" class="cub"/>
+            <link field="notes" reltype="has_many" map="" key="item" class="cubin"/>
 		</links>
 	</class>
+	<class id="cubin" controller="open-ils.cstore" oils_obj:fieldmapper="container::user_bucket_item_note" oils_persist:tablename="container.user_bucket_item_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.user_bucket_item_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="item" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="item" reltype="has_a" key="id" map="" class="cubi"/>
+		</links>
+	</class>
 	<class id="mus" controller="open-ils.cstore" oils_obj:fieldmapper="money::user_summary" oils_persist:tablename="money.usr_summary">
 		<fields oils_persist:primary="usr" oils_persist:sequence="">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -3295,7 +3434,7 @@
             </actions>
         </permacrud>
 	</class>
-	<class id="asva" controller="open-ils.cstore" oils_obj:fieldmapper="action::survey_answer" oils_persist:tablename="action.survey_answer" reporter:label="Survey Answer">
+	<class id="asva" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::survey_answer" oils_persist:tablename="action.survey_answer" reporter:label="Survey Answer">
 		<fields oils_persist:primary="id" oils_persist:sequence="action.survey_answer_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -3446,6 +3585,19 @@
 			<link field="items" reltype="has_many" key="bucket" map="" class="ccbi"/>
 		</links>
 	</class>
+	<class id="ccbn" controller="open-ils.cstore" oils_obj:fieldmapper="container::copy_bucket_note" oils_persist:tablename="container.copy_bucket_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.copy_bucket_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="bucket" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="bucket" reltype="has_a" key="id" map="" class="ccb"/>
+		</links>
+	</class>
 	<class id="puwoum" controller="open-ils.cstore" oils_obj:fieldmapper="permission::usr_work_ou_map" oils_persist:tablename="permission.usr_work_ou_map">
 		<fields oils_persist:primary="id" oils_persist:sequence="permission.usr_work_ou_map_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -3616,12 +3768,28 @@
 			<field name="id" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="id" />
 			<field name="target_biblio_record_entry" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="link"/>
 			<field name="create_time" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="timestamp" />
+			<field name="pos" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="int" />
+			<field name="notes" oils_obj:array_position="8" oils_persist:virtual="true" reporter:datatype="link" />
 		</fields>
 		<links>
 			<link field="target_biblio_record_entry" reltype="has_a" key="id" map="" class="bre"/>
 			<link field="bucket" reltype="has_a" key="id" map="" class="cbreb"/>
+            <link field="notes" reltype="has_many" map="" key="item" class="cbrebin"/>
 		</links>
 	</class>
+	<class id="cbrebin" controller="open-ils.cstore" oils_obj:fieldmapper="container::biblio_record_entry_bucket_item_note" oils_persist:tablename="container.biblio_record_entry_bucket_item_note">
+		<fields oils_persist:primary="id" oils_persist:sequence="container.biblio_record_entry_bucket_item_note_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="item" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="note" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text" />
+		</fields>
+		<links>
+			<link field="item" reltype="has_a" key="id" map="" class="cbrebi"/>
+		</links>
+	</class>
 	<class id="asce" controller="open-ils.cstore" oils_obj:fieldmapper="asset::stat_cat_entry" oils_persist:tablename="asset.stat_cat_entry" reporter:label="Item Stat Cat Entry">
 		<fields oils_persist:primary="id" oils_persist:sequence="asset.stat_cat_entry_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -3637,6 +3805,27 @@
 			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
 		</links>
 	</class>
+
+	<class id="ahrcc" controller="open-ils.cstore open-ils.reporter-store open-ils.pcrud" oils_obj:fieldmapper="action::hold_request_cancel_cause" oils_persist:tablename="action.hold_request_cancel_cause" oils_persist:restrict_primary="100">
+		<fields oils_persist:primary="id" oils_persist:sequence="action.hold_request_cancel_cause_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field reporter:label="Cause ID" name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field reporter:label="Cause Label" name="label" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
+		</fields>
+		<links/>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_HOLD_CANCEL_CAUSE" global_required="true"/>
+                <retrieve/>
+                <update permission="ADMIN_HOLD_CANCEL_CAUSE" global_required="true"/>
+                <delete permission="ADMIN_HOLD_CANCEL_CAUSE" global_required="true"/>
+            </actions>
+        </permacrud>
+	</class>
+
+
 	<class id="ahtc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::hold_transit_copy" oils_persist:tablename="action.hold_transit_copy" reporter:core="true" reporter:label="Hold Transit">
 		<fields oils_persist:primary="id" oils_persist:sequence="action.transit_copy_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -3732,6 +3921,29 @@
             </actions>
         </permacrud>
 	</class>
+	<class id="fdoc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::idl_field_doc" oils_persist:tablename="config.idl_field_doc" oils_persist:field_safe="true">
+		<fields oils_persist:primary="id" oils_persist:sequence="config.idl_field_doc_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id" />
+			<field name="fm_class" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text"/>
+			<field name="field" oils_obj:array_position="5" oils_persist:virtual="false" reporter:datatype="text"/>
+			<field name="owner" oils_obj:array_position="6" oils_persist:virtual="false" reporter:datatype="org_unit"/>
+			<field name="string" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true"/>
+		</fields>
+		<links>
+			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
+		</links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create   permission="ADMIN_FIELD_DOC" context_field="owner"/>
+                <retrieve/>
+                <update   permission="ADMIN_FIELD_DOC" context_field="owner"/>
+                <delete   permission="ADMIN_FIELD_DOC" context_field="owner"/>
+            </actions>
+        </permacrud>
+	</class>
 	<class id="i18n_l" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::i18n_locale" oils_persist:tablename="config.i18n_locale" oils_persist:field_safe="true">
 		<fields oils_persist:primary="code">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
@@ -3752,7 +3964,7 @@
             </actions>
         </permacrud>
 	</class>
-	<class id="cbt" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::billing_type" oils_persist:tablename="config.billing_type">
+	<class id="cbt" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::billing_type" oils_persist:tablename="config.billing_type" oils_persist:restrict_primary="100">
 		<fields oils_persist:primary="id" oils_persist:sequence="config.billing_type_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -3779,7 +3991,7 @@
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
 			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
-			<field reporter:label="Currency Code" name="code" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="text" />
+			<field reporter:label="Currency Code" name="code" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="text" reporter:selector='label'/>
 			<field reporter:label="Currency Label" name="label" oils_obj:array_position="4" oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true" />
 		</fields>
 		<links/>
@@ -3793,7 +4005,7 @@
         </permacrud>
 	</class>
 
-	<class id="acqexr" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::exchange_rate" oils_persist:tablename="acq.exchange_rate">
+	<class id="acqexr" controller="open-ils.cstore open-ils.pcrud open-ils.reporter-store" oils_obj:fieldmapper="acq::exchange_rate" oils_persist:tablename="acq.exchange_rate">
 		<fields oils_persist:primary="id" oils_persist:sequence="acq.exchange_rate_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -3807,9 +4019,17 @@
 			<link field="from_currency" reltype="has_a" key="code" map="" class="acqct"/>
 			<link field="to_currency" reltype="has_a" key="code" map="" class="acqct"/>
 		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="ADMIN_CURRENCY_TYPE" global_required="true"/>
+				<retrieve/>
+				<update permission="ADMIN_CURRENCY_TYPE" global_required="true"/>
+				<delete permission="ADMIN_CURRENCY_TYPE" global_required="true"/>
+			</actions>
+		</permacrud>
 	</class>
 
-	<class id="acqpro" controller="open-ils.cstore open-ils.reporter-store" oils_obj:fieldmapper="acq::provider" oils_persist:tablename="acq.provider">
+	<class id="acqpro" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="acq::provider" oils_persist:tablename="acq.provider" reporter:label="Provider">
 		<fields oils_persist:primary="id" oils_persist:sequence="acq.provider_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
 			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
@@ -3824,8 +4044,124 @@
 			<link field="currency_type" reltype="has_a" key="code" map="" class="acqct"/>
 			<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
 		</links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create   permission="ADMIN_PROVIDER" context_field="owner"/>
+                <retrieve permission="ADMIN_PROVIDER" context_field="owner"/>
+                <update   permission="ADMIN_PROVIDER" context_field="owner"/>
+                <delete   permission="ADMIN_PROVIDER" context_field="owner"/>
+            </actions>
+        </permacrud>
 	</class>
 
+	<class id="acqpa" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="acq::provider_address" oils_persist:tablename="acq.provider_address" reporter:label="Provider Address">
+		<fields oils_persist:primary="id" oils_persist:sequence="acq.provider_address_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="address_type" oils_obj:array_position="3" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="city" oils_obj:array_position="4" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="country" oils_obj:array_position="5" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="county" oils_obj:array_position="6" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="id" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="id"/>
+			<field name="provider" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="post_code" oils_obj:array_position="9" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="state" oils_obj:array_position="10" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="street1" oils_obj:array_position="11" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="street2" oils_obj:array_position="12" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field name="valid" oils_obj:array_position="13" oils_persist:virtual="false" reporter:datatype="bool"/>
+		</fields>
+		<links>
+			<link field="provider" reltype="has_a" key="id" map="" class="acqpro"/>
+		</links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </create>
+                <retrieve permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </retrieve>
+                <update permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </update>
+                <delete permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </delete>
+            </actions>
+        </permacrud>
+	</class>
+
+	<class id="acqpc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="acq::provider_contact" oils_persist:tablename="acq.provider_contact" reporter:label="Provider Contact">
+		<fields oils_persist:primary="id" oils_persist:sequence="acq.provider_contact_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field name="id" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="id"/>
+			<field name="provider" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field name="name" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="text"/>
+			<field name="role" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="text"/>
+			<field name="email" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="text"/>
+			<field name="phone" oils_obj:array_position="3" oils_persist:virtual="false" reporter:datatype="text"/>
+        </fields>
+		<links>
+			<link field="provider" reltype="has_a" key="id" map="" class="acqpro"/>
+		</links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </create>
+                <retrieve permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </retrieve>
+                <update permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </update>
+                <delete permission="ADMIN_PROVIDER">
+                    <context link="provider" field="owner"/>
+                </delete>
+            </actions>
+        </permacrud>
+    </class>
+	<class id="acqpca" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="acq::provider_contact_address" oils_persist:tablename="acq.provider_contact_address" reporter:label="Provider Contact Address">
+		<fields oils_persist:primary="id" oils_persist:sequence="acq.provider_contact_address_id_seq">
+			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
+			<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
+			<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
+			<field reporter:label="Type" name="address_type" oils_obj:array_position="3" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field reporter:label="City" name="city" oils_obj:array_position="4" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field reporter:label="Country" name="country" oils_obj:array_position="5" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field reporter:label="County" name="county" oils_obj:array_position="6" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field reporter:label="Address ID" name="id" oils_obj:array_position="7" oils_persist:virtual="false" reporter:datatype="id" />
+			<field reporter:label="Postal Code" name="post_code" oils_obj:array_position="8" oils_persist:virtual="false" reporter:datatype="text"/>
+			<field reporter:label="State" name="state" oils_obj:array_position="9" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field reporter:label="Street (1)" name="street1" oils_obj:array_position="10" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field reporter:label="Street (2)" name="street2" oils_obj:array_position="11" oils_persist:virtual="false"  reporter:datatype="text"/>
+			<field reporter:label="Contact" name="contact" oils_obj:array_position="12" oils_persist:virtual="false" reporter:datatype="link"/>
+			<field reporter:label="Valid Address?" name="valid" oils_obj:array_position="13" oils_persist:virtual="false" reporter:datatype="bool"/>
+		</fields>
+		<links>
+			<link field="contact" reltype="has_a" key="id" map="" class="acqpc"/>
+		</links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_PROVIDER">
+                    <context link="contact" jump='provider' field="owner"/>
+                </create>
+                <retrieve permission="ADMIN_PROVIDER">
+                    <context link="contact" jump='provider' field="owner"/>
+                </retrieve>
+                <update permission="ADMIN_PROVIDER">
+                    <context link="contact" jump='provider' field="owner"/>
+                </update>
+                <delete permission="ADMIN_PROVIDER">
+                    <context link="contact" jump='provider' field="owner"/>
+                </delete>
+            </actions>
+        </permacrud>
+	</class>
+
 	<class id="acqfs" controller="open-ils.cstore open-ils.reporter-store open-ils.pcrud" oils_obj:fieldmapper="acq::funding_source" oils_persist:tablename="acq.funding_source">
 		<fields oils_persist:primary="id" oils_persist:sequence="acq.funding_source_id_seq">
 			<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />

Modified: branches/staff-client-experiment/Open-ILS/examples/opensrf.xml.example
===================================================================
--- branches/staff-client-experiment/Open-ILS/examples/opensrf.xml.example	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/examples/opensrf.xml.example	2009-02-19 05:39:53 UTC (rev 12228)
@@ -252,6 +252,25 @@
         </cache>
 
         <apps>
+            <!-- Acquisitions server -->
+            <open-ils.acq>
+                <keepalive>5</keepalive>
+                <stateless>1</stateless>
+                <language>perl</language>
+                <implementation>OpenILS::Application::Acq</implementation>
+                <max_requests>100</max_requests>
+                <unix_config>
+                    <unix_sock>open-ils.acq_unix.sock</unix_sock>
+                    <unix_pid>open-ils.acq_unix.pid</unix_pid>
+                    <unix_log>open-ils.acq_unix.log</unix_log>
+                    <max_requests>100</max_requests>
+                    <min_children>1</min_children>
+                    <max_children>15</max_children>
+                    <min_spare_children>1</min_spare_children>
+                    <max_spare_children>5</max_spare_children>
+                </unix_config>
+            </open-ils.acq>
+
             <!-- Authentication server -->
             <open-ils.auth>
 
@@ -327,7 +346,7 @@
                 </unix_config>
                 <app_settings>
                     <marc_html_xsl>oilsMARC21slim2HTML.xsl</marc_html_xsl>
-					<marc_html_xsl_slim>oilsMARC21slim2HTMLslim.xsl</marc_html_xsl_slim>
+                    <marc_html_xsl_slim>oilsMARC21slim2HTMLslim.xsl</marc_html_xsl_slim>
 
                     <!-- Default to using staged search -->
                     <use_staged_search>true</use_staged_search>
@@ -832,7 +851,7 @@
                 <keepalive>5</keepalive>
                 <stateless>1</stateless>
                 <language>perl</language>
-				<implementation>OpenILS::Application::Vandelay</implementation>
+                <implementation>OpenILS::Application::Vandelay</implementation>
                 <max_requests>100</max_requests>
                 <unix_config>
                     <unix_sock>vandelay_unix.sock</unix_sock>
@@ -875,6 +894,7 @@
                 <appname>opensrf.settings</appname> 
                 <appname>opensrf.math</appname> 
                 <appname>opensrf.dbmath</appname> 
+                <appname>open-ils.acq</appname> 
                 <appname>open-ils.cat</appname> 
                 <appname>open-ils.supercat</appname> 
                 <appname>open-ils.search</appname> 

Modified: branches/staff-client-experiment/Open-ILS/examples/opensrf_core.xml.example
===================================================================
--- branches/staff-client-experiment/Open-ILS/examples/opensrf_core.xml.example	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/examples/opensrf_core.xml.example	2009-02-19 05:39:53 UTC (rev 12228)
@@ -20,11 +20,13 @@
         <services>
           <service>opensrf.math</service>
           <service>open-ils.actor</service>
+          <service>open-ils.acq</service>
           <service>open-ils.auth</service>
           <service>open-ils.cat</service>
           <service>open-ils.circ</service>
           <service>open-ils.collections</service>
           <service>open-ils.fielder</service>
+          <service>open-ils.pcrud</service>
           <service>open-ils.permacrud</service>
           <service>open-ils.reporter</service>
           <service>open-ils.search</service>

Modified: branches/staff-client-experiment/Open-ILS/src/Makefile.am
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/Makefile.am	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/Makefile.am	2009-02-19 05:39:53 UTC (rev 12228)
@@ -243,12 +243,20 @@
 	cp -r @top_srcdir@/Open-ILS/web/. $(DESTDIR)$(WEBDIR)
 	cp @top_srcdir@/Open-ILS/xsl/*.xsl $(opacextrasdir)
 	cp @top_srcdir@/Open-ILS/xsl/*.xsl $(XSLDIR)
+	cp -r $(DESTDIR)$(WEBDIR)/opac/skin/default/* $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/
+	cp -r @top_srcdir@/Open-ILS/web/opac/skin/craftsman/* $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/
 	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/mresult.xml
 	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/rresult.xml
 	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/rdetail.xml
 	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/advanced.xml
 	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/myopac.xml
 	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/cnbrowse.xml
+	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/mresult.xml
+	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/rresult.xml
+	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/rdetail.xml
+	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/advanced.xml
+	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/myopac.xml
+	ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/cnbrowse.xml
 
 
 offline-install:

Modified: branches/staff-client-experiment/Open-ILS/src/c-apps/Makefile.am
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/c-apps/Makefile.am	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/c-apps/Makefile.am	2009-02-19 05:39:53 UTC (rev 12228)
@@ -7,7 +7,7 @@
 AM_CFLAGS = $(DEF_CFLAGS) -DOSRF_LOG_PARAMS -I at top_srcdir@/include/
 AM_LDFLAGS = $(DEF_LDFLAGS) -L$(DBI_LIBS) -lopensrf 
 
-bin_PROGRAMS = oils_dataloader dump_idl
+bin_PROGRAMS = oils_dataloader dump_idl test_json_query
 oils_dataloader_SOURCES = oils_dataloader.c
 oils_dataloader_LDFLAGS = $(AM_LDFLAGS) -loils_idl
 oils_dataloader_DEPENDENCIES = liboils_idl.la liboils_utils.la
@@ -16,6 +16,11 @@
 dump_idl_LDFLAGS = $(AM_LDFLAGS) -loils_idl
 dump_idl_DEPENDENCIES = liboils_idl.la liboils_utils.la
 
+test_json_query_SOURCES = test_json_query.c
+test_json_query_LDFLAGS = $(AM_LDFLAGS) -loils_idl
+test_json_query_LDADD = oils_cstore.la
+test_json_query_DEPENDENCIES = liboils_idl.la liboils_utils.la
+
 lib_LTLIBRARIES = liboils_idl.la liboils_utils.la oils_cstore.la oils_rstore.la oils_pcrud.la oils_auth.la
 
 liboils_idl_la_SOURCES = oils_idl-core.c

Modified: branches/staff-client-experiment/Open-ILS/src/c-apps/dump_idl.c
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/c-apps/dump_idl.c	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/c-apps/dump_idl.c	2009-02-19 05:39:53 UTC (rev 12228)
@@ -141,6 +141,8 @@
 				printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr );
 			else if( !strcmp( attr_name, "tablename" ) )
 				printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr );
+			else if( !strcmp( attr_name, "restrict_primary" ) )
+				printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr );
 			else if( !strcmp( attr_name, "virtual" ) )
 				printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr );
 			else if( !strcmp( attr_name, "controller" ) )

Modified: branches/staff-client-experiment/Open-ILS/src/c-apps/oils_cstore.c
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/c-apps/oils_cstore.c	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/c-apps/oils_cstore.c	2009-02-19 05:39:53 UTC (rev 12228)
@@ -61,17 +61,20 @@
 static char* searchFieldTransform ( const char*, osrfHash*, const jsonObject*);
 static char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObject*, const char* );
 static char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* );
-static char* searchINPredicate ( const char*, osrfHash*, const jsonObject*, const char* );
-static char* searchPredicate ( const char*, osrfHash*, jsonObject* );
+static char* searchINPredicate ( const char*, osrfHash*,
+								 jsonObject*, const char*, osrfMethodContext* );
+static char* searchPredicate ( const char*, osrfHash*, jsonObject*, osrfMethodContext* );
 static char* searchJOIN ( const jsonObject*, osrfHash* );
 static char* searchWHERE ( const jsonObject*, osrfHash*, int, osrfMethodContext* );
 static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
 
-static char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
+char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int );
 
 void userDataFree( void* );
 static void sessionDataFree( char*, void* );
 static char* getSourceDefinition( osrfHash* );
+static int str_is_true( const char* str );
+static int obj_is_true( const jsonObject* obj );
 
 #ifdef PCRUD
 static jsonObject* verifyUserPCRUD( osrfMethodContext* );
@@ -186,11 +189,10 @@
             continue;
         }
 
-        char* virt = osrfHashGet(idlClass, "virtual");
-        if (virt && !strcmp( virt, "true")) {
-            osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
-            continue;
-        }
+		if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
+			osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
+			continue;
+		}
 
         // Look up some other attributes of the current class
         const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
@@ -217,8 +219,7 @@
             if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue;
 #endif
 
-            if (    readonly &&
-                    !strncasecmp( "true", readonly, 4) &&
+            if (    str_is_true( readonly ) &&
                     ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd')
                ) continue;
 
@@ -357,11 +358,10 @@
         osrfHash* class = osrfHashGet( oilsIDL(), classname );
         osrfHash* fields = osrfHashGet( class, "fields" );
 
-        char* virt = osrfHashGet(class, "virtual");
-        if (virt && !strcmp( virt, "true")) {
-            osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
-            continue;
-        }
+		if( str_is_true( osrfHashGet(class, "virtual") ) ) {
+			osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
+			continue;
+		}
 
         char* tabledef = getSourceDefinition(class);
 
@@ -470,7 +470,10 @@
 }
 
 int beginTransaction ( osrfMethodContext* ctx ) {
-    OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
 
 #ifdef PCRUD
     jsonObject* user = verifyUserPCRUD( ctx );
@@ -501,7 +504,10 @@
 }
 
 int setSavepoint ( osrfMethodContext* ctx ) {
-    OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
 
     int spNamePos = 0;
 #ifdef PCRUD
@@ -546,9 +552,12 @@
 }
 
 int releaseSavepoint ( osrfMethodContext* ctx ) {
-    OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
 
-    int spNamePos = 0;
+	int spNamePos = 0;
 #ifdef PCRUD
     spNamePos = 1;
     jsonObject* user = verifyUserPCRUD( ctx );
@@ -591,9 +600,12 @@
 }
 
 int rollbackSavepoint ( osrfMethodContext* ctx ) {
-    OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
 
-    int spNamePos = 0;
+	int spNamePos = 0;
 #ifdef PCRUD
     spNamePos = 1;
     jsonObject* user = verifyUserPCRUD( ctx );
@@ -636,7 +648,10 @@
 }
 
 int commitTransaction ( osrfMethodContext* ctx ) {
-    OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
 
 #ifdef PCRUD
     jsonObject* user = verifyUserPCRUD( ctx );
@@ -664,7 +679,10 @@
 }
 
 int rollbackTransaction ( osrfMethodContext* ctx ) {
-    OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
 
 #ifdef PCRUD
     jsonObject* user = verifyUserPCRUD( ctx );
@@ -692,9 +710,12 @@
 }
 
 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
-    OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
 
-    osrfHash* meta = (osrfHash*) ctx->method->userData;
+	osrfHash* meta = (osrfHash*) ctx->method->userData;
     osrfHash* class_obj = osrfHashGet( meta, "class" );
 
     int err = 0;
@@ -763,7 +784,7 @@
             jsonObjectSetIndex( _p, 1, jsonNewObjectType(JSON_HASH) );
         }
 
-        jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "no_i18n", jsonParseString("true") );
+		jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "no_i18n", jsonNewBoolObject( 1 ) );
 
         jsonObjectSetKey(
             jsonObjectGetIndex( _p, 1 ),
@@ -913,7 +934,6 @@
     jsonObjectFree(user);
 
     osrfStringArray* permission = osrfHashGet(pcrud, "permission");
-    char* global_required = osrfHashGet(pcrud, "global_required");
     osrfStringArray* local_context = osrfHashGet(pcrud, "local_context");
     osrfHash* foreign_context = osrfHashGet(pcrud, "foreign_context");
 
@@ -921,7 +941,7 @@
 
     int err = 0;
     char* pkey_value = NULL;
-    if (global_required && !strcmp( "true", global_required )) {
+	if ( str_is_true( osrfHashGet(pcrud, "global_required") ) ) {
 	    osrfLogDebug( OSRF_LOG_MARK, "global-level permissions required, fetching top of the org tree" );
 
         // check for perm at top of org tree
@@ -1290,7 +1310,9 @@
 		return jsonNULL;
 	}
 
-	if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
+	// The following test is harmless but redundant.  If a class is
+	// readonly, we don't register a create method for it.
+	if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
 		osrfAppSessionStatus(
 			ctx->session,
 			OSRF_STATUS_BADREQUEST,
@@ -1338,7 +1360,8 @@
 
 		osrfHash* field = osrfHashGet( fields, field_name );
 
-		if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
+		if( str_is_true( osrfHashGet( field, "virtual" ) ) )
+			continue;
 
 		const jsonObject* field_object = oilsFMGetObject( target, field_name );
 
@@ -1458,7 +1481,7 @@
 				quiet_str = jsonObjectToSimpleString( quiet_obj );
 		}
 
-		if( quiet_str && !strcmp( quiet_str, "true" )) {  // if quietness is specified
+		if( str_is_true( quiet_str ) ) {  // if quietness is specified
 			obj = jsonNewObject(id);
 		}
 		else {
@@ -1508,8 +1531,6 @@
 
 	osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" );
 
-	jsonObject* obj;
-
 	char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, id_pos));
 	jsonObject* order_hash = jsonObjectGetIndex(ctx->params, order_pos);
 
@@ -1541,7 +1562,7 @@
 		return jsonNULL;
 	}
 
-	obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
+	jsonObject* obj = jsonObjectClone( jsonObjectGetIndex(list, 0) );
 
 	jsonObjectFree( list );
 	jsonObjectFree( fake_params );
@@ -1591,7 +1612,7 @@
 }
 
 static char* searchINPredicate (const char* class, osrfHash* field,
-		const jsonObject* node, const char* op) {
+		jsonObject* node, const char* op, osrfMethodContext* ctx ) {
 	growing_buffer* sql_buf = buffer_init(32);
 	
 	buffer_fadd(
@@ -1609,76 +1630,100 @@
 		buffer_add(sql_buf, "IN (");
 	}
 
-	int in_item_index = 0;
-	int in_item_first = 1;
-	jsonObject* in_item;
-	while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
+    if (node->type == JSON_HASH) {
+        // subquery predicate
+        char* subpred = SELECT(
+            ctx,
+            jsonObjectGetKey( node, "select" ),
+            jsonObjectGetKey( node, "from" ),
+            jsonObjectGetKey( node, "where" ),
+            jsonObjectGetKey( node, "having" ),
+            jsonObjectGetKey( node, "order_by" ),
+            jsonObjectGetKey( node, "limit" ),
+            jsonObjectGetKey( node, "offset" ),
+            SUBSELECT
+        );
 
-		if (in_item_first)
-			in_item_first = 0;
-		else
-			buffer_add(sql_buf, ", ");
+        buffer_add(sql_buf, subpred);
+        free(subpred);
 
-		if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
-			char* val = jsonNumberToDBString( field, in_item );
-			OSRF_BUFFER_ADD( sql_buf, val );
-			free(val);
-
-		} else {
-			char* key_string = jsonObjectToSimpleString(in_item);
-			if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
-				OSRF_BUFFER_ADD( sql_buf, key_string );
-				free(key_string);
-			} else {
-				osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
-				free(key_string);
-				buffer_free(sql_buf);
-				return NULL;
-			}
-		}
-	}
-
+    } else if (node->type == JSON_ARRAY) {
+        // litteral value list
+    	int in_item_index = 0;
+    	int in_item_first = 1;
+    	jsonObject* in_item;
+    	while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) {
+    
+    		if (in_item_first)
+    			in_item_first = 0;
+    		else
+    			buffer_add(sql_buf, ", ");
+    
+    		if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
+    			char* val = jsonNumberToDBString( field, in_item );
+    			OSRF_BUFFER_ADD( sql_buf, val );
+    			free(val);
+    
+    		} else {
+    			char* key_string = jsonObjectToSimpleString(in_item);
+    			if ( dbi_conn_quote_string(dbhandle, &key_string) ) {
+    				OSRF_BUFFER_ADD( sql_buf, key_string );
+    				free(key_string);
+    			} else {
+    				osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string);
+    				free(key_string);
+    				buffer_free(sql_buf);
+    				return NULL;
+    			}
+    		}
+    	}
+    }
+    
 	OSRF_BUFFER_ADD_CHAR( sql_buf, ')' );
 
 	return buffer_release(sql_buf);
 }
 
+// Receive a JSON_ARRAY representing a function call.  The first
+// entry in the array is the function name.  The rest are parameters.
 static char* searchValueTransform( const jsonObject* array ) {
 	growing_buffer* sql_buf = buffer_init(32);
 
 	char* val = NULL;
-	int func_item_index = 0;
-	int func_item_first = 2;
 	jsonObject* func_item;
+	
+	// Get the function name
+	if( array->size > 0 ) {
+		func_item = jsonObjectGetIndex( array, 0 );
+		val = jsonObjectToSimpleString( func_item );
+		OSRF_BUFFER_ADD( sql_buf, val );
+		OSRF_BUFFER_ADD( sql_buf, "( " );
+		free(val);
+	}
+	
+	// Get the parameters
+	int func_item_index = 1;   // We already grabbed the zeroth entry
 	while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) {
 
-		val = jsonObjectToSimpleString(func_item);
+		// Add a separator comma, if we need one
+		if( func_item_index > 2 )
+			buffer_add( sql_buf, ", " );
 
-		if (func_item_first == 2) {
-			OSRF_BUFFER_ADD(sql_buf, val);
-			OSRF_BUFFER_ADD(sql_buf, "( ");
-			free(val);
-			func_item_first--;
-			continue;
-		}
-
-		if (func_item_first)
-			func_item_first--;
-		else
-			buffer_add(sql_buf, ", ");
-
+		// Add the current parameter
 		if (func_item->type == JSON_NULL) {
 			buffer_add( sql_buf, "NULL" );
-		} else if ( dbi_conn_quote_string(dbhandle, &val) ) {
-			OSRF_BUFFER_ADD( sql_buf, val );
 		} else {
-			osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
-			free(val);
-			buffer_free(sql_buf);
-			return NULL;
+			val = jsonObjectToSimpleString(func_item);
+			if ( dbi_conn_quote_string(dbhandle, &val) ) {
+				OSRF_BUFFER_ADD( sql_buf, val );
+				free(val);
+			} else {
+				osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
+				buffer_free(sql_buf);
+				free(val);
+				return NULL;
+			}
 		}
-
-		free(val);
 	}
 
 	buffer_add( sql_buf, " )" );
@@ -1706,12 +1751,18 @@
 	return buffer_release(sql_buf);
 }
 
+// class is a class name
+// field is a field definition as stored in the IDL
+// node comes from the method parameter, and represents an entry in the SELECT list
 static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) {
 	growing_buffer* sql_buf = buffer_init(32);
 	
 	char* field_transform = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "transform" ) );
 	char* transform_subcolumn = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "result_field" ) );
 
+	if(transform_subcolumn)
+		OSRF_BUFFER_ADD_CHAR( sql_buf, '(' );    // enclose transform in parentheses
+
 	if (field_transform) {
 		buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name"));
 	    const jsonObject* array = jsonObjectGetKeyConst( node, "params" );
@@ -1730,36 +1781,24 @@
 					OSRF_BUFFER_ADD( sql_buf, val );
         		} else {
 	        		osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val);
-		    	    free(field_transform);
+					free(transform_subcolumn);
+					free(field_transform);
 					free(val);
         			buffer_free(sql_buf);
 	        		return NULL;
     	    	}
 				free(val);
 			}
-
         }
 
-       	buffer_add(
-        	sql_buf,
-	        " )"
-       	);
+		buffer_add( sql_buf, " )" );
 
 	} else {
 		buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name"));
 	}
 
-    if (transform_subcolumn) {
-        char * tmp = buffer_release(sql_buf);
-        sql_buf = buffer_init(32);
-        buffer_fadd(
-            sql_buf,
-            "(%s).\"%s\"",
-            tmp,
-            transform_subcolumn
-        );
-        free(tmp);
-    }
+    if (transform_subcolumn)
+        buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn );
  
 	if (field_transform) free(field_transform);
 	if (transform_subcolumn) free(transform_subcolumn);
@@ -1890,11 +1929,12 @@
 	return buffer_release(sql_buf);
 }
 
-static char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) {
+static char* searchPredicate ( const char* class, osrfHash* field, 
+							   jsonObject* node, osrfMethodContext* ctx ) {
 
 	char* pred = NULL;
 	if (node->type == JSON_ARRAY) { // equality IN search
-		pred = searchINPredicate( class, field, node, NULL );
+		pred = searchINPredicate( class, field, node, NULL, ctx );
 	} else if (node->type == JSON_HASH) { // non-equality search
 		jsonObject* pred_node;
 		jsonIterator* pred_itr = jsonNewIterator( node );
@@ -1902,7 +1942,7 @@
 			if ( !(strcasecmp( pred_itr->key,"between" )) )
 				pred = searchBETWEENPredicate( class, field, pred_node );
 			else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) )
-				pred = searchINPredicate( class, field, pred_node, pred_itr->key );
+				pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx );
 			else if ( pred_node->type == JSON_ARRAY )
 				pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key );
 			else if ( pred_node->type == JSON_HASH )
@@ -1971,21 +2011,28 @@
 		free(_tmp);
 		working_hash = freeable_hash;
 	}
-	else
+	else {
+		if( join_hash->type != JSON_HASH ) {
+			osrfLogError(
+				OSRF_LOG_MARK,
+				"%s: JOIN failed; expected JSON object type not found",
+				MODULENAME
+			);
+			return NULL;
+		}
 		working_hash = join_hash;
+	}
 
 	growing_buffer* join_buf = buffer_init(128);
-	char* leftclass = osrfHashGet(leftmeta, "classname");
+	const char* leftclass = osrfHashGet(leftmeta, "classname");
 
 	jsonObject* snode = NULL;
 	jsonIterator* search_itr = jsonNewIterator( working_hash );
-	if(freeable_hash)
-		jsonObjectFree(freeable_hash);
 	
 	while ( (snode = jsonIteratorNext( search_itr )) ) {
 		osrfHash* idlClass = osrfHashGet( oilsIDL(), search_itr->key );
 
-		char* class = osrfHashGet(idlClass, "classname");
+		const char* class = osrfHashGet(idlClass, "classname");
 
 		char* fkey = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "fkey" ) );
 		char* field = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "field" ) );
@@ -2002,6 +2049,8 @@
 					leftclass
 				);
 				buffer_free(join_buf);
+				if(freeable_hash)
+					jsonObjectFree(freeable_hash);
 				free(field);
 				jsonIteratorFree(search_itr);
 				return NULL;
@@ -2020,6 +2069,8 @@
 					class
 				);
 				buffer_free(join_buf);
+				if(freeable_hash)
+					jsonObjectFree(freeable_hash);
 				free(fkey);
 				jsonIteratorFree(search_itr);
 				return NULL;
@@ -2029,37 +2080,46 @@
 		} else if (!field && !fkey) {
 			osrfHash* _links = oilsIDLFindPath("/%s/links", leftclass);
 
-			int i = 0;
-			osrfStringArray* keys = osrfHashKeys( _links );
-			while ( (fkey = osrfStringArrayGetString(keys, i++)) ) {
-				fkey = strdup(osrfStringArrayGetString(keys, i++));
-				if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", leftclass, fkey), class) ) {
-					field = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey) );
+			// For each link defined for the left class:
+			// see if the link references the joined class
+			osrfHashIterator* itr = osrfNewHashIterator( _links );
+			osrfHash* curr_link = NULL;
+			while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
+				const char* other_class = osrfHashGet( curr_link, "class" );
+				if( other_class && !strcmp( other_class, class ) ) {
+
+					// Found a link between the classes
+					fkey = strdup( osrfHashIteratorKey( itr ) );
+					const char* other_key = osrfHashGet( curr_link, "key" );
+					field = other_key ? strdup( other_key ) : NULL;
 					break;
-				} else {
-					free(fkey);
 				}
 			}
-			osrfStringArrayFree(keys);
-
-			if (!field && !fkey) {
+			osrfHashIteratorFree( itr );
+			
+			if (!field || !fkey) {
+				// Do another such search, with the classes reversed
 				_links = oilsIDLFindPath("/%s/links", class);
 
-				i = 0;
-				keys = osrfHashKeys( _links );
-				while ( (field = osrfStringArrayGetString(keys, i++)) ) {
-					field = strdup(osrfStringArrayGetString(keys, i++));
-					if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", class, field), class) ) {
-						fkey = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", class, field) );
+				// For each link defined for the joined class:
+				// see if the link references the left class
+				osrfHashIterator* itr = osrfNewHashIterator( _links );
+				osrfHash* curr_link = NULL;
+				while( (curr_link = osrfHashIteratorNext( itr ) ) ) {
+					const char* other_class = osrfHashGet( curr_link, "class" );
+					if( other_class && !strcmp( other_class, leftclass ) ) {
+
+						// Found a link between the classes
+						fkey = strdup( osrfHashIteratorKey( itr ) );
+						const char* other_key = osrfHashGet( curr_link, "key" );
+						field = other_key ? strdup( other_key ) : NULL;
 						break;
-					} else {
-						free(field);
 					}
 				}
-				osrfStringArrayFree(keys);
+				osrfHashIteratorFree( itr );
 			}
 
-			if (!field && !fkey) {
+			if (!field || !fkey) {
 				osrfLogError(
 					OSRF_LOG_MARK,
 					"%s: JOIN failed.  No link defined between %s and %s",
@@ -2068,6 +2128,8 @@
 					class
 				);
 				buffer_free(join_buf);
+				if(freeable_hash)
+					jsonObjectFree(freeable_hash);
 				jsonIteratorFree(search_itr);
 				return NULL;
 			}
@@ -2091,7 +2153,8 @@
 		free(type);
 
 		char* table = getSourceDefinition(idlClass);
-		buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey);
+		buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s",
+					table, class, class, field, leftclass, fkey);
 		free(table);
 
 		const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" );
@@ -2128,7 +2191,9 @@
 		free(field);
 	}
 
-    jsonIteratorFree(search_itr);
+	if(freeable_hash)
+		jsonObjectFree(freeable_hash);
+	jsonIteratorFree(search_itr);
 
 	return buffer_release(join_buf);
 }
@@ -2145,7 +2210,7 @@
 
 	osrfLogDebug(
         OSRF_LOG_MARK,
-        "%s: Entering searchWHERE; search_hash addr = %d, meta addr = %d, opjoin_type = %d, ctx addr = %d",
+        "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p",
         MODULENAME,
         search_hash,
         meta,
@@ -2246,7 +2311,7 @@
                     char* table = getSourceDefinition(meta);
                     osrfLogError(
                         OSRF_LOG_MARK,
-                        "%s: Attempt to reference non-existant column %s on %s (%s)",
+                        "%s: Attempt to reference non-existent column %s on %s (%s)",
                         MODULENAME,
                         search_itr->key,
                         table,
@@ -2258,7 +2323,7 @@
 					return NULL;
                 }
 
-                char* subpred = searchPredicate( class, field, node );
+                char* subpred = searchPredicate( class, field, node, ctx );
                 buffer_add( sql_buf, subpred );
                 free(subpred);
             }
@@ -2283,7 +2348,7 @@
 	return buffer_release(sql_buf);
 }
 
-static char* SELECT (
+char* SELECT (
 		/* method context */ osrfMethodContext* ctx,
 		
 		/* SELECT   */ jsonObject* selhash,
@@ -2302,12 +2367,10 @@
 
 	// general tmp objects
 	const jsonObject* tmp_const;
-	jsonObject* _tmp = NULL;
 	jsonObject* selclass = NULL;
 	jsonObject* selfield = NULL;
 	jsonObject* snode = NULL;
 	jsonObject* onode = NULL;
-	jsonObject* found = NULL;
 
 	char* string = NULL;
 	int from_function = 0;
@@ -2320,8 +2383,6 @@
 
 	// metadata about the core search class
 	osrfHash* core_meta = NULL;
-	osrfHash* core_fields = NULL;
-	osrfHash* idlClass = NULL;
 
 	// punt if there's no core class
 	if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size ))
@@ -2334,18 +2395,27 @@
 		
 		core_class = strdup( tmp_itr->key );
 		join_hash = snode;
+		
+		jsonObject* extra = jsonIteratorNext( tmp_itr );
 
 		jsonIteratorFree( tmp_itr );
 		snode = NULL;
+		
+		// There shouldn't be more than one entry in join_hash
+		if( extra )
+			return NULL;	// Malformed join_hash; extra entry
 
 	} else if (join_hash->type == JSON_ARRAY) {
-        from_function = 1;
-        selhash = NULL;
+		from_function = 1;
+		core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) );
+		selhash = NULL;
 
 	} else if (join_hash->type == JSON_STRING) {
 		core_class = jsonObjectToSimpleString( join_hash );
 		join_hash = NULL;
 	}
+	else
+		return NULL;
 
 	// punt if we don't know about the core class (and it's not a function)
 	if (!from_function && !(core_meta = osrfHashGet( oilsIDL(), core_class ))) {
@@ -2376,153 +2446,175 @@
 	growing_buffer* group_buf = buffer_init(128);
 	growing_buffer* having_buf = buffer_init(128);
 
-	if (!from_function) 
-        core_fields = osrfHashGet(core_meta, "fields");
+	// Build a select list
+	if(from_function)   // From a function we select everything
+		OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
+	else {
 
-	// ... and if we /are/ building the default list, do that
-	if ( (_tmp = jsonObjectGetKey(selhash,core_class)) && !_tmp->size ) {
-		
-		int i = 0;
-		char* field;
+		// If we need to build a default list, do so
+		jsonObject* _tmp = jsonObjectGetKey( selhash, core_class );
+		if ( _tmp && !_tmp->size ) {
 
-        if (!from_function) {
+			int i = 0;
+			char* field;
+			osrfHash* core_fields = osrfHashGet( core_meta, "fields" );
+
     		osrfStringArray* keys = osrfHashKeys( core_fields );
-	    	while ( (field = osrfStringArrayGetString(keys, i++)) ) {
-		    	if ( strncasecmp( "true", osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ), 4 ) )
-			    	jsonObjectPush( _tmp, jsonNewObject( field ) );
+			while ( (field = osrfStringArrayGetString(keys, i++)) ) {
+				if( ! str_is_true( osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ) ) )
+					jsonObjectPush( _tmp, jsonNewObject( field ) );   // not virtual; use it
     		}
-	    	osrfStringArrayFree(keys);
-        }
-	}
-
-	// Now we build the actual select list
-	if (!from_function) {
+			osrfStringArrayFree(keys);
+		}
+	
+		// Now build the actual select list
 	    int sel_pos = 1;
 	    jsonObject* is_agg = jsonObjectFindPath(selhash, "//aggregate");
 	    first = 1;
 	    gfirst = 1;
 	    jsonIterator* selclass_itr = jsonNewIterator( selhash );
-	    while ( (selclass = jsonIteratorNext( selclass_itr )) ) {
+	    while ( (selclass = jsonIteratorNext( selclass_itr )) ) {    // For each class
 
 		    // round trip through the idl, just to be safe
-		    idlClass = osrfHashGet( oilsIDL(), selclass_itr->key );
-		    if (!idlClass) continue;
-		    char* cname = osrfHashGet(idlClass, "classname");
+			const char* cname = selclass_itr->key;
+			osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
+		    if (!idlClass)
+				// No such class.  Skip it.
+				continue;
 
-		    // make sure the target relation is in the join tree
-		    if (strcmp(core_class,cname)) {
-			    if (!join_hash) continue;
+		    // Make sure the target relation is in the join tree.
+			
+			// At this point join_hash is a step down from the join_hash we
+			// received as a parameter.  If the original was a JSON_STRING,
+			// then json_hash is now NULL.  If the original was a JSON_HASH,
+			// then json_hash is now the first (and only) entry in it,
+			// denoting the core class.  We've already excluded the
+			// possibility that the original was a JSON_ARRAY, because in
+			// that case from_function would be non-NULL, and we wouldn't
+			// be here.
 
-			    if (join_hash->type == JSON_STRING) {
+		    if ( strcmp( core_class, cname )) {
+			    if (!join_hash) 
+					// There's only one class in the FROM clause,
+					// and this isn't it.  Skip it.
+					continue;
+
+				if (join_hash->type == JSON_STRING) {
 				    string = jsonObjectToSimpleString(join_hash);
-				    found = strcmp(string,cname) ? NULL : jsonParseString("{\"1\":\"1\"}");
+					int different = strcmp( string, cname );
 				    free(string);
-			    } else {
-				    found = jsonObjectFindPath(join_hash, "//%s", cname);
-			    }
-
-			    if (!found->size) {
-				    jsonObjectFree(found);
-				    continue;
-			    }
-
-			    jsonObjectFree(found);
+					if ( different )
+						// There's only one class in the FROM clause,
+						// and this isn't it.  Skip it.
+						continue;
+				} else {
+				    jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname);
+					unsigned long size = found->size;
+					jsonObjectFree( found );
+					if ( 0 == size )
+						// No such class anywhere in the join tree.  Skip it.
+						continue;
+				}
 		    }
 
+			// Look up some attributes of the current class, so that we 
+			// don't have to look them up again for each field
+			osrfHash* class_field_set = osrfHashGet( idlClass, "fields" );
+			char* class_pkey = osrfHashGet( idlClass, "primarykey" );
+			char* class_tname = osrfHashGet( idlClass, "tablename" );
+			
 		    // stitch together the column list ...
 		    jsonIterator* select_itr = jsonNewIterator( selclass );
-		    while ( (selfield = jsonIteratorNext( select_itr )) ) {
+		    while ( (selfield = jsonIteratorNext( select_itr )) ) {   // for each SELECT column
 
-			    char* __column = NULL;
-			    char* __alias = NULL;
+				// If we need a separator comma, add one
+				if (first) {
+					first = 0;
+				} else {
+					OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
+				}
 
-			    // ... if it's a sstring, just toss it on the pile
+			    // ... if it's a string, just toss it on the pile
 			    if (selfield->type == JSON_STRING) {
 
 				    // again, just to be safe
-				    char* _requested_col = jsonObjectToSimpleString(selfield);
-				    osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), _requested_col );
-				    free(_requested_col);
+					const char* _requested_col = selfield->value.s;
+				    osrfHash* field = osrfHashGet( class_field_set, _requested_col );
+				    if (!field) continue;		// No such field in current class; skip it
 
-				    if (!field) continue;
-				    __column = strdup(osrfHashGet(field, "name"));
+					const char* col_name = osrfHashGet(field, "name");
 
-				    if (first) {
-					    first = 0;
-				    } else {
-						OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
-				    }
-
                     if (locale) {
-            		    char* i18n = osrfHashGet(field, "i18n");
+            		    const char* i18n;
 			            if (flags & DISABLE_I18N)
                             i18n = NULL;
+						else
+							i18n = osrfHashGet(field, "i18n");
 
-    	    		    if ( i18n && !strncasecmp("true", i18n, 4)) {
-        	                char* pkey = osrfHashGet(idlClass, "primarykey");
-        	                char* tname = osrfHashGet(idlClass, "tablename");
-
-                            buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, cname, __column, pkey, cname, pkey, locale, __column);
+						if( str_is_true( i18n ) ) {
+                            buffer_fadd( select_buf,
+								" oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
+								class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name );
                         } else {
-				            buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
+				            buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
                         }
                     } else {
-				        buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column);
+				        buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name );
                     }
-
+					
 			    // ... but it could be an object, in which case we check for a Field Transform
 			    } else {
 
-				    __column = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield, "column" ) );
+				    char* _column = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield, "column" ) );
 
-				    // again, just to be safe
-				    osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), __column );
-				    if (!field) continue;
-				    const char* fname = osrfHashGet(field, "name");
+				    // Get the field definition from the IDL
+				    osrfHash* field = osrfHashGet( class_field_set, _column );
+				    if (!field) continue;         // No such field defined in IDL.  Skip it.
 
-				    if (first) {
-					    first = 0;
-				    } else {
-						OSRF_BUFFER_ADD_CHAR( select_buf, ',' );
+					// Decide what to use as a column alias
+					char* _alias;
+					if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
+						_alias = jsonObjectToSimpleString( tmp_const );
+						free(_column);
+					} else {         // Use column name as its own alias
+						_alias = _column;
 					}
+					_column = NULL;   // To emphasize that we're through with _column
 
-				    if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) {
-					    __alias = jsonObjectToSimpleString( tmp_const );
-				    } else {
-					    __alias = strdup(__column);
-				    }
+					if (jsonObjectGetKeyConst( selfield, "transform" )) {
+						char* transform_str = searchFieldTransform(cname, field, selfield);
+						buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias);
+						free(transform_str);
+					} else {
 
-				    if (jsonObjectGetKeyConst( selfield, "transform" )) {
-					    free(__column);
-					    __column = searchFieldTransform(cname, field, selfield);
-					    buffer_fadd(select_buf, " %s AS \"%s\"", __column, __alias);
-				    } else {
+						const char* fname = osrfHashGet(field, "name");
+
                         if (locale) {
-                		    char* i18n = osrfHashGet(field, "i18n");
+                		    const char* i18n;
 			                if (flags & DISABLE_I18N)
                                 i18n = NULL;
-    
-    	        		    if ( i18n && !strncasecmp("true", i18n, 4)) {
-        	                    char* pkey = osrfHashGet(idlClass, "primarykey");
-        	                    char* tname = osrfHashGet(idlClass, "tablename");
+							else
+								i18n = osrfHashGet(field, "i18n");
 
-                                buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, cname, fname, pkey, cname, pkey, locale, __alias);
+							if( str_is_true( i18n ) ) {
+                                buffer_fadd( select_buf,
+									" oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"",
+		 							class_tname, cname, fname, class_pkey, cname, class_pkey, locale, _alias);
                             } else {
-					            buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
+					            buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, _alias);
                             }
                         } else {
-					        buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias);
+					        buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, _alias);
                         }
 				    }
+
+				    free(_alias);
 			    }
 
 			    if (is_agg->size || (flags & SELECT_DISTINCT)) {
 
-				    if ( !(
-                            jsonBoolIsTrue( jsonObjectGetKey( selfield, "aggregate" ) ) ||
-	                        ((int)jsonObjectGetNumber(jsonObjectGetKey( selfield, "aggregate" ))) == 1 // support 1/0 for perl's sake
-                         )
-                    ) {
+					const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" );
+				    if ( ! obj_is_true( aggregate_obj ) ) {
 					    if (gfirst) {
 						    gfirst = 0;
 					    } else {
@@ -2538,35 +2630,30 @@
 							OSRF_BUFFER_ADD_CHAR( group_buf, ',' );
 					    }
 
-					    __column = searchFieldTransform(cname, field, selfield);
+					    _column = searchFieldTransform(cname, field, selfield);
 						OSRF_BUFFER_ADD_CHAR(group_buf, ' ');
-						OSRF_BUFFER_ADD(group_buf, __column);
-					    __column = searchFieldTransform(cname, field, selfield);
+						OSRF_BUFFER_ADD(group_buf, _column);
+					    _column = searchFieldTransform(cname, field, selfield);
 					*/
 				    }
 			    }
 
-			    if (__column) free(__column);
-			    if (__alias) free(__alias);
-
 			    sel_pos++;
-		    }
+		    } // end while -- iterating across SELECT columns
 
-            // jsonIteratorFree(select_itr);
-	    }
+            jsonIteratorFree(select_itr);
+	    } // end while -- iterating across classes
 
-        // jsonIteratorFree(selclass_itr);
+        jsonIteratorFree(selclass_itr);
 
 	    if (is_agg) jsonObjectFree(is_agg);
-    } else {
-		OSRF_BUFFER_ADD_CHAR( select_buf, '*' );
     }
 
 
 	char* col_list = buffer_release(select_buf);
 	char* table = NULL;
-    if (!from_function) table = getSourceDefinition(core_meta);
-    else table = searchValueTransform(join_hash);
+	if (from_function) table = searchValueTransform(join_hash);
+	else table = getSourceDefinition(core_meta);
 
 	// Put it all together
 	buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class );
@@ -2581,14 +2668,18 @@
 		    free(join_clause);
 	    }
 
+		// Build a WHERE clause, if there is one
 	    if ( search_hash ) {
 		    buffer_add(sql_buf, " WHERE ");
 
-		    // and it's on the the WHERE clause
+		    // and it's on the WHERE clause
 		    char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx );
 
-		    if (!pred) {
-			    if (ctx) {
+		    if (pred) {
+				buffer_add(sql_buf, pred);
+				free(pred);
+			} else {
+				if (ctx) {
 			        osrfAppSessionStatus(
 				        ctx->session,
 				        OSRF_STATUS_INTERNALSERVERERROR,
@@ -2604,20 +2695,21 @@
 			    buffer_free(sql_buf);
 			    if (defaultselhash) jsonObjectFree(defaultselhash);
 			    return NULL;
-		    } else {
-			    buffer_add(sql_buf, pred);
-			    free(pred);
 		    }
         }
 
+		// Build a HAVING clause, if there is one
 	    if ( having_hash ) {
 		    buffer_add(sql_buf, " HAVING ");
 
 		    // and it's on the the WHERE clause
 		    char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx );
 
-		    if (!pred) {
-			    if (ctx) {
+		    if (pred) {
+				buffer_add(sql_buf, pred);
+				free(pred);
+			} else {
+				if (ctx) {
 			        osrfAppSessionStatus(
 				        ctx->session,
 				        OSRF_STATUS_INTERNALSERVERERROR,
@@ -2633,12 +2725,10 @@
 			    buffer_free(sql_buf);
 			    if (defaultselhash) jsonObjectFree(defaultselhash);
 			    return NULL;
-		    } else {
-			    buffer_add(sql_buf, pred);
-			    free(pred);
 		    }
 	    }
 
+		// Build an ORDER BY clause, if there is one
 	    first = 1;
 	    jsonIterator* class_itr = jsonNewIterator( order_hash );
 	    while ( (snode = jsonIteratorNext( class_itr )) ) {
@@ -2704,7 +2794,7 @@
 					    buffer_add(order_buf, direction);
 				    }
 
-			    }
+			    } // end while
                 // jsonIteratorFree(order_itr);
 
 		    } else if ( snode->type == JSON_ARRAY ) {
@@ -2726,7 +2816,7 @@
 				    buffer_add(order_buf, _f);
 				    free(_f);
 
-			    }
+			    } // end while
                 // jsonIteratorFree(order_itr);
 
 
@@ -2753,10 +2843,10 @@
 			    return NULL;
 		    }
 
-	    }
-    }
+	    } // end while
+		// jsonIteratorFree(class_itr);
+	}
 
-    // jsonIteratorFree(class_itr);
 
 	string = buffer_release(group_buf);
 
@@ -2839,7 +2929,7 @@
 
 		osrfStringArray* keys = osrfHashKeys( fields );
 		while ( (field = osrfStringArrayGetString(keys, i++)) ) {
-			if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
+			if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) )
 				jsonObjectPush( flist, jsonNewObject( field ) );
 		}
 		osrfStringArrayFree(keys);
@@ -2881,13 +2971,14 @@
 			}
 
             if (locale) {
-        		char* i18n = osrfHashGet(field, "i18n");
-			    if (
-                    jsonBoolIsTrue( jsonObjectGetKey( order_hash, "no_i18n" ) ) ||
-                    ((int)jsonObjectGetNumber(jsonObjectGetKey( order_hash, "no_i18n" ))) == 1 // support 1/0 for perl's sake
-                ) i18n = NULL;
+        		const char* i18n;
+				const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" );
+				if ( obj_is_true( no_i18n_obj ) )    // Suppress internationalization?
+					i18n = NULL;
+				else
+					i18n = osrfHashGet(field, "i18n");
 
-    			if ( i18n && !strncasecmp("true", i18n, 4)) {
+				if( str_is_true( i18n ) ) {
         	        char* pkey = osrfHashGet(idlClass, "primarykey");
         	        char* tname = osrfHashGet(idlClass, "tablename");
 
@@ -3066,7 +3157,11 @@
 }
 
 int doJSONSearch ( osrfMethodContext* ctx ) {
-	OSRF_METHOD_VERIFY_CONTEXT(ctx);
+	if(osrfMethodVerifyContext( ctx )) {
+		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+		return -1;
+	}
+
 	osrfLogDebug(OSRF_LOG_MARK, "Recieved query request");
 
 	int err = 0;
@@ -3076,20 +3171,14 @@
 
 	jsonObject* hash = jsonObjectGetIndex(ctx->params, 0);
 
-    int flags = 0;
+	int flags = 0;
 
-	if (jsonBoolIsTrue(jsonObjectGetKey( hash, "distinct" )))
-         flags |= SELECT_DISTINCT;
+	if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
+		flags |= SELECT_DISTINCT;
 
-	if ( ((int)jsonObjectGetNumber(jsonObjectGetKey( hash, "distinct" ))) == 1 ) // support 1/0 for perl's sake
-         flags |= SELECT_DISTINCT;
+	if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
+		flags |= DISABLE_I18N;
 
-	if (jsonBoolIsTrue(jsonObjectGetKey( hash, "no_i18n" )))
-         flags |= DISABLE_I18N;
-
-	if ( ((int)jsonObjectGetNumber(jsonObjectGetKey( hash, "no_i18n" ))) == 1 ) // support 1/0 for perl's sake
-         flags |= DISABLE_I18N;
-
 	osrfLogDebug(OSRF_LOG_MARK, "Building SQL ...");
 	char* sql = SELECT(
 			ctx,
@@ -3173,38 +3262,11 @@
 	}
 	
 	osrfLogDebug(OSRF_LOG_MARK, "%s SQL =  %s", MODULENAME, sql);
+
 	dbi_result result = dbi_conn_query(dbhandle, sql);
-
-	jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
-	if(result) {
-		osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
-		osrfHash* dedup = osrfNewHash();
-
-		if (dbi_result_first_row(result)) {
-			/* JSONify the result */
-			osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
-			do {
-				obj = oilsMakeFieldmapperFromResult( result, meta );
-				char* pkey_val = oilsFMGetString( obj, pkey );
-				if ( osrfHashGet( dedup, pkey_val ) ) {
-					jsonObjectFree(obj);
-					free(pkey_val);
-				} else {
-					osrfHashSet( dedup, pkey_val, pkey_val );
-					jsonObjectPush(res_list, obj);
-				}
-			} while (dbi_result_next_row(result));
-		} else {
-			osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
-		}
-
-		osrfHashFree(dedup);
-
-		/* clean up the query */
-		dbi_result_free(result); 
-
-	} else {
-		osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
+	if( NULL == result ) {
+		osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]",
+			MODULENAME, osrfHashGet(meta, "fieldmapper"), sql);
 		osrfAppSessionStatus(
 			ctx->session,
 			OSRF_STATUS_INTERNALSERVERERROR,
@@ -3214,11 +3276,37 @@
 		);
 		*err = -1;
 		free(sql);
-		jsonObjectFree(res_list);
 		return jsonNULL;
 
+	} else {
+		osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
 	}
 
+	jsonObject* res_list = jsonNewObjectType(JSON_ARRAY);
+	osrfHash* dedup = osrfNewHash();
+
+	if (dbi_result_first_row(result)) {
+		/* JSONify the result */
+		osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
+		do {
+			obj = oilsMakeFieldmapperFromResult( result, meta );
+			char* pkey_val = oilsFMGetString( obj, pkey );
+			if ( osrfHashGet( dedup, pkey_val ) ) {
+				jsonObjectFree(obj);
+				free(pkey_val);
+			} else {
+				osrfHashSet( dedup, pkey_val, pkey_val );
+				jsonObjectPush(res_list, obj);
+			}
+		} while (dbi_result_next_row(result));
+	} else {
+		osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s",
+			MODULENAME, sql );
+	}
+
+	osrfHashFree(dedup);
+	/* clean up the query */
+	dbi_result_free(result);
 	free(sql);
 
 	if (res_list->size && order_hash) {
@@ -3477,7 +3565,9 @@
 		return jsonNULL;
 	}
 
-	if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
+	// The following test is harmless but redundant.  If a class is
+	// readonly, we don't register an update method for it.
+	if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
 		osrfAppSessionStatus(
 			ctx->session,
 			OSRF_STATUS_BADREQUEST,
@@ -3526,7 +3616,8 @@
 		osrfHash* field = osrfHashGet( fields, field_name );
 
 		if(!( strcmp( field_name, pkey ) )) continue;
-		if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue;
+		if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) )
+			continue;
 
 		const jsonObject* field_object = oilsFMGetObject( target, field_name );
 
@@ -3635,7 +3726,9 @@
 		return jsonNULL;
 	}
 
-	if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) {
+	// The following test is harmless but redundant.  If a class is
+	// readonly, we don't register a delete method for it.
+	if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) {
 		osrfAppSessionStatus(
 			ctx->session,
 			OSRF_STATUS_BADREQUEST,
@@ -3746,11 +3839,13 @@
 
 		/* fetch the fieldmapper index */
 		if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
-			char* virt = (char*)osrfHashGet(_f, "virtual");
-			char* pos = (char*)osrfHashGet(_f, "array_position");
+			
+			if ( str_is_true( osrfHashGet(_f, "virtual") ) )
+				continue;
+			
+			const char* pos = (char*)osrfHashGet(_f, "array_position");
+			if ( !pos ) continue;
 
-			if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
-
 			fmIndex = atoi( pos );
 			osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
 		} else {
@@ -3903,3 +3998,36 @@
 	return object;
 }
 
+// Interpret a string as true or false
+static int str_is_true( const char* str ) {
+	if( NULL == str || strcasecmp( str, "true" ) )
+		return 0;
+	else
+		return 1;
+}
+
+// Interpret a jsonObject as true or false
+static int obj_is_true( const jsonObject* obj ) {
+	if( !obj )
+		return 0;
+	else switch( obj->type )
+	{
+		case JSON_BOOL :
+			if( obj->value.b )
+				return 1;
+			else
+				return 0;
+		case JSON_STRING :
+			if( strcasecmp( obj->value.s, "true" ) )
+				return 0;
+			else
+				return 1;
+		case JSON_NUMBER :          // Support 1/0 for perl's sake
+			if( jsonObjectGetNumber( obj ) == 1.0 )
+				return 1;
+			else
+				return 0;
+		default :
+			return 0;
+	}
+}

Modified: branches/staff-client-experiment/Open-ILS/src/c-apps/oils_idl-core.c
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/c-apps/oils_idl-core.c	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/c-apps/oils_idl-core.c	2009-02-19 05:39:53 UTC (rev 12228)
@@ -66,6 +66,15 @@
 				);
 			}
 
+			if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "restrict_primary", BAD_CAST PERSIST_NS))) {
+				osrfLogDebug(OSRF_LOG_MARK, "Delete restriction policy set at '%s' for pkey of class %s", prop_str, current_class_name );
+				osrfHashSet(
+					class_def_hash,
+					strdup( prop_str ),
+					"restrict_primary"
+				);
+			}
+
 			if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "virtual", BAD_CAST PERSIST_NS))) {
 				osrfHashSet(
 					class_def_hash,

Copied: branches/staff-client-experiment/Open-ILS/src/c-apps/test_json_query.c (from rev 12227, trunk/Open-ILS/src/c-apps/test_json_query.c)
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/c-apps/test_json_query.c	                        (rev 0)
+++ branches/staff-client-experiment/Open-ILS/src/c-apps/test_json_query.c	2009-02-19 05:39:53 UTC (rev 12228)
@@ -0,0 +1,261 @@
+/*
+Copyright (C) 2009  Georgia Public Library Service 
+Scott McKellar <scott at esilibrary.com>
+
+	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.
+
+	Description : Translates a JSON query into SQL and writes the
+	results to standard output.  Synopsis:
+
+	test_json_query [-i IDL_file] [-f file_name] [-v] query
+	
+	-i supplies the name of the IDL file.  If no IDL file is specified,
+	   json_test_query uses the value of the environmental variable
+	   OILS_IDL_FILENAME, if it is defined, or defaults to
+	   "/openils/conf/fm_IDL.xml".
+
+	-f supplies the name of a text file containing the JSON query to
+	   be translated.  A file name constisting of a single hyphen
+	   denotes standard input.  If this option is present, all
+	   non-option arguments are ignored.
+
+	-v verbose; outputs the name of the IDL file and the text of the
+	   JSON query.
+
+	If there is no -f option supplied, json_query translates the 
+	first non-option parameter.  This parameter is subject to the
+	usual mangling by the shell.  In most cases it will be sufficient
+	to enclose it in single quotes, but of course any single quotes
+	embedded within the query will need to be escaped.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <opensrf/utils.h>
+#include <opensrf/osrf_json.h>
+#include <opensrf/osrf_application.h>
+#include <opensrf/osrf_app_session.h>
+#include <openils/oils_idl.h>
+
+#define DISABLE_I18N	2
+#define SELECT_DISTINCT 1
+
+// Prototype for SELECT(), which is not in any header
+char* SELECT (
+		/* method context */ osrfMethodContext* ctx,
+		
+		/* SELECT   */ jsonObject* selhash,
+		/* FROM     */ jsonObject* join_hash,
+		/* WHERE    */ jsonObject* search_hash,
+		/* HAVING   */ jsonObject* having_hash,
+		/* ORDER BY */ jsonObject* order_hash,
+		/* LIMIT    */ jsonObject* limit,
+		/* OFFSET   */ jsonObject* offset,
+		/* flags    */ int flags
+);
+
+static int obj_is_true( const jsonObject* obj );
+static int test_json_query( const char* json_query );
+static char* load_query( const char* filename );
+
+int main( int argc, char* argv[] ) {
+
+	// Parse command line
+
+	const char* idl_file_name = NULL;
+	const char* query_file_name = NULL;
+	int verbose = 0;                        // boolean
+
+	int opt;
+	opterr = 0;
+	const char optstring[] = ":f:i:v";
+
+	while( ( opt = getopt( argc, argv, optstring ) ) != -1 ) {
+		switch( opt )
+		{
+			case 'f' :  // get file name of query
+				if( query_file_name ) {
+					fprintf( stderr, "Multiple input files not allowed\n" );
+					return EXIT_FAILURE;
+				}
+				else
+					query_file_name = optarg;
+				break;
+			case 'i' :  // get name of IDL file
+				if( idl_file_name ) {
+					fprintf( stderr, "Multiple IDL file names not allowed\n" );
+					return EXIT_FAILURE;
+				}
+				else
+					idl_file_name = optarg;
+				break;
+			case 'v' :  // Verbose
+				verbose = 1;
+				break;
+			case '?' :  // Invalid option
+				fprintf( stderr, "Invalid option '-%c' on command line\n",
+						 (char) optopt );
+				return EXIT_FAILURE;
+			default :  // Huh?
+				fprintf( stderr, "Internal error: unexpected value '%c'"
+						"for optopt", (char) optopt );
+				return EXIT_FAILURE;
+
+		}
+	}
+
+	// If the command line doesn't specify an IDL file, get it
+	// from an environmental variable, or apply a default
+	if( NULL == idl_file_name ) {
+		idl_file_name = getenv( "OILS_IDL_FILENAME" );
+		if( NULL == idl_file_name )
+			idl_file_name = "/openils/conf/fm_IDL.xml";
+	}
+
+	if( verbose )
+		printf( "IDL file: %s\n", idl_file_name );
+
+	char* loaded_json = NULL;
+	const char* json_query = NULL;
+
+	// Get the JSON query into a string
+	if( query_file_name ) {   // Got a file?  Load it
+		if( optind < argc )
+			fprintf( stderr, "Extra parameter(s) ignored\n" );
+		loaded_json = load_query( query_file_name );
+		if( !loaded_json ) 
+			return EXIT_FAILURE;
+		json_query = loaded_json;
+	} else {                  // No file?  Use command line parameter
+		if ( optind == argc ) {
+			fprintf( stderr, "No JSON query specified\n" );
+			return EXIT_FAILURE;
+		} else
+			json_query = argv[ optind ];
+	}
+
+	if( verbose )
+		printf( "JSON query: %s\n", json_query );
+
+	osrfLogSetLevel( OSRF_LOG_WARNING );    // Suppress informational messages
+	(void) oilsIDLInit( idl_file_name );    // Load IDL into memory
+
+	// Translate the JSON into SQL
+	int rc = test_json_query( json_query );
+
+	if( loaded_json )
+		free( loaded_json );
+
+	return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static int test_json_query( const char* json_query ) {
+
+	jsonObject* hash = jsonParseString( json_query );
+	if( !hash ) {
+		fprintf( stderr, "Invalid JSON\n" );
+		return -1;
+	}
+	
+	int flags = 0;
+
+	if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) )
+		flags |= SELECT_DISTINCT;
+
+	if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) )
+		flags |= DISABLE_I18N;
+
+	char* sql_query = SELECT(
+		NULL,
+		jsonObjectGetKey( hash, "select" ),
+		jsonObjectGetKey( hash, "from" ),
+		jsonObjectGetKey( hash, "where" ),
+		jsonObjectGetKey( hash, "having" ),
+		jsonObjectGetKey( hash, "order_by" ),
+		jsonObjectGetKey( hash, "limit" ),
+		jsonObjectGetKey( hash, "offset" ),
+		flags
+	);
+
+	if ( !sql_query ) {
+		fprintf( stderr, "Invalid query\n" );
+		return -1;
+	}
+	else
+		printf( "%s\n", sql_query );
+
+	free( sql_query );
+	jsonObjectFree( hash );
+	return 0;
+}
+
+// Interpret a jsonObject as true or false
+static int obj_is_true( const jsonObject* obj ) {
+	if( !obj )
+		return 0;
+	else switch( obj->type )
+	{
+		case JSON_BOOL :
+			if( obj->value.b )
+				return 1;
+			else
+				return 0;
+		case JSON_STRING :
+			if( strcasecmp( obj->value.s, "true" ) )
+				return 0;
+			else
+				return 1;
+			case JSON_NUMBER :          // Support 1/0 for perl's sake
+				if( jsonObjectGetNumber( obj ) == 1.0 )
+					return 1;
+				else
+					return 0;
+		default :
+			return 0;
+	}
+}
+
+static char* load_query( const char* filename ) {
+	FILE* fp;
+
+	// Sanity check
+	if( ! filename || ! *filename ) {
+		fprintf( stderr, "Name of query file is empty or missing\n" );
+		return NULL;
+	}
+
+	// Open query file, or use standard input
+	if( ! strcmp( filename, "-" ) )
+		fp = stdin;
+	else {
+		fp = fopen( filename, "r" );
+		if( !fp ) {
+			fprintf( stderr, "Unable to open query file \"%s\"\n", filename );
+			return NULL;
+		}
+	}
+
+	// Load file into a growing_buffer
+	size_t num_read;
+	char buf[ BUFSIZ + 1 ];
+	growing_buffer* gb = buffer_init( sizeof( buf ) );
+
+    while( ( num_read = fread( buf, 1, sizeof( buf ) - 1, fp ) ) ) {
+		buf[ num_read ] = '\0';
+		buffer_add( gb, buf );
+	}
+
+	if( fp != stdin )
+		fclose( fp );
+
+	return buffer_release( gb );
+}

Modified: branches/staff-client-experiment/Open-ILS/src/extras/fast-extract
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/extras/fast-extract	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/extras/fast-extract	2009-02-19 05:39:53 UTC (rev 12228)
@@ -58,6 +58,7 @@
 SELECT  id,
         tcn_source,
         tcn_value,
+        deleted,
         REGEXP_REPLACE(marc, E'\\n','','g') AS marc
   FROM  biblio.record_entry
   WHERE id = ?

Modified: branches/staff-client-experiment/Open-ILS/src/extras/ils_events.xml
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/extras/ils_events.xml	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/extras/ils_events.xml	2009-02-19 05:39:53 UTC (rev 12228)
@@ -719,6 +719,9 @@
 	<event code='1712' textcode='AUTH_QUEUE_EXISTS'>
 		<desc xml:lang="en-US">An authority record queue with the same name already exists</desc>
 	</event>
+	<event code='1713' textcode='SURVEY_RESPONSES_EXIST'>
+		<desc xml:lang="en-US">Responses to this survey exist</desc>
+	</event>
 
 
 	<event code='2000' textcode='BAD_PARAMS'>

Modified: branches/staff-client-experiment/Open-ILS/src/extras/org_tree_html_options.pl
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/extras/org_tree_html_options.pl	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/extras/org_tree_html_options.pl	2009-02-19 05:39:53 UTC (rev 12228)
@@ -6,6 +6,7 @@
 use OpenILS::Utils::Fieldmapper;
 use OpenSRF::Utils::SettingsClient;
 use Unicode::Normalize;
+use Data::Dumper;
 
 die "usage: perl org_tree_html_options.pl <bootstrap_config> <output_file>" unless $ARGV[1];
 OpenSRF::System->bootstrap_client(config_file => $ARGV[0]);
@@ -17,6 +18,12 @@
 my $ses = OpenSRF::AppSession->create("open-ils.actor");
 my $tree = $ses->request("open-ils.actor.org_tree.retrieve")->gather(1);
 
+my @types;
+my $aout = $ses->request("open-ils.actor.org_types.retrieve")->gather(1);
+foreach my $type (@$aout) {
+	$types[int($type->id)] = $type;
+}
+
 print_option($tree);
 
 $ses->disconnect();
@@ -28,7 +35,7 @@
 	my $node = shift;
 	return unless ($node->opac_visible =~ /^[y1t]+/i);
 
-	my $depth = $node->ou_type - 1;
+	my $depth = $types[$node->ou_type]->depth;
 	my $sname = entityize($node->shortname);
 	my $name = entityize($node->name);
 	my $kids = $node->children;

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -17,10 +17,15 @@
 my $svc = 'open-ils.cstore';
 my $meth = 'open-ils.cstore.direct.container';
 my %types;
+my %ctypes;
 $types{'biblio'} = "$meth.biblio_record_entry_bucket";
 $types{'callnumber'} = "$meth.call_number_bucket";
 $types{'copy'} = "$meth.copy_bucket";
 $types{'user'} = "$meth.user_bucket";
+$ctypes{'biblio'} = "container_biblio_record_entry_bucket";
+$ctypes{'callnumber'} = "container_call_number_bucket";
+$ctypes{'copy'} = "container_copy_bucket";
+$ctypes{'user'} = "container_user_bucket";
 my $event;
 
 sub _sort_buckets {
@@ -40,22 +45,20 @@
 	NOTES
 
 sub bucket_retrieve_all {
-	my($self, $client, $authtoken, $userid) = @_;
+	my($self, $client, $auth, $user_id) = @_;
+    my $e = new_editor(authtoken => $auth);
+    return $e->event unless $e->checkauth;
 
-	my( $staff, $evt ) = $apputils->checkses($authtoken);
-	return $evt if $evt;
-
-	my( $user, $e ) = $apputils->checkrequestor( $staff, $userid, 'VIEW_CONTAINER');
-	return $e if $e;
-
-	$logger->debug("User " . $staff->id . 
-		" retrieving all buckets for user $userid");
-
+    if($e->requestor->id ne $user_id) {
+        return $e->event unless $e->allowed('VIEW_CONTAINER');
+    }
+    
 	my %buckets;
+    for my $type (keys %ctypes) {
+        my $meth = "search_" . $ctypes{$type};
+	    $buckets{$type} = $e->$meth({owner => $user_id});
+    }
 
-	$buckets{$_} = $apputils->simplereq( 
-		$svc, $types{$_} . ".search.atomic", { owner => $userid } ) for keys %types;
-
 	return \%buckets;
 }
 
@@ -63,67 +66,88 @@
 	method	=> "bucket_flesh",
 	api_name	=> "open-ils.actor.container.flesh",
 	argc		=> 3, 
-	notes		=> <<"	NOTES");
-		Fleshes a bucket by id
-		PARAMS(authtoken, bucketClass, bucketId)
-		bucketclasss include biblio, callnumber, copy, and user.  
-		bucketclass defaults to biblio.
-		If requestor ID is different than bucketOwnerId, requestor must have
-		VIEW_CONTAINER permissions.
-	NOTES
+);
 
+__PACKAGE__->register_method(
+	method	=> "bucket_flesh_pub",
+	api_name	=> "open-ils.actor.container.public.flesh",
+	argc		=> 3, 
+);
+
 sub bucket_flesh {
+	my($self, $conn, $auth, $class, $bucket_id) = @_;
+    my $e = new_editor(authtoken => $auth);
+    return $e->event unless $e->checkauth;
+    return _bucket_flesh($self, $conn, $e, $class, $bucket_id);
+}
 
-	my($self, $client, $authtoken, $class, $bucket) = @_;
+sub bucket_flesh_pub {
+    my($self, $conn, $class, $bucket_id) = @_;
+    my $e = new_editor();
+    return _bucket_flesh($self, $conn, $e, $class, $bucket_id);
+}
 
-	my( $staff, $evt ) = $apputils->checkses($authtoken);
-	return $evt if $evt;
+sub _bucket_flesh {
+	my($self, $conn, $e, $class, $bucket_id) = @_;
+	my $meth = 'retrieve_' . $ctypes{$class};
+    my $bkt = $e->$meth($bucket_id) or return $e->event;
 
-	$logger->debug("User " . $staff->id . " retrieving bucket $bucket");
-
-	my $meth = $types{$class};
-
-	my $bkt = $apputils->simplereq( $svc, "$meth.retrieve", $bucket );
-	#if(!$bkt) {return undef};
-	return OpenILS::Event->new('CONTAINER_NOT_FOUND', payload=>$bucket) unless $bkt;
-
-	if(!$bkt->pub) {
-		my( $user, $e ) = $apputils->checkrequestor( $staff, $bkt->owner, 'VIEW_CONTAINER' );
-		return $e if $e;
+	unless($U->is_true($bkt->pub)) {
+        return undef if $self->api_name =~ /public/;
+        unless($bkt->owner eq $e->requestor->id) {
+            return $e->event unless $e->allowed('VIEW_CONTAINER', $bkt);
+        }
 	}
 
-	$bkt->items( $apputils->simplereq( $svc,
-		"$meth"."_item.search.atomic", { bucket => $bucket } ) );
+    my $fmclass = $bkt->class_name . "i";
+    $meth = 'search_' . $ctypes{$class} . '_item';
+	$bkt->items(
+        $e->$meth(
+            {bucket => $bucket_id}, 
+            {   order_by => {$fmclass => "pos"},
+                flesh => 1, 
+                flesh_fields => {cbrebi => ['notes']}
+            }
+        )
+    );
 
 	return $bkt;
 }
 
 
 __PACKAGE__->register_method(
-	method	=> "bucket_flesh_public",
-	api_name	=> "open-ils.actor.container.public.flesh",
-	argc		=> 3, 
-	notes		=> <<"	NOTES");
-		Fleshes a bucket by id
-		PARAMS(authtoken, bucketClass, bucketId)
-		bucketclasss include biblio, callnumber, copy, and user.  
-		bucketclass defaults to biblio.
-		If requestor ID is different than bucketOwnerId, requestor must have
-		VIEW_CONTAINER permissions.
-	NOTES
+	method	=> "item_note_cud",
+	api_name	=> "open-ils.actor.container.item_note.cud",
+);
 
-sub bucket_flesh_public {
 
-	my($self, $client, $class, $bucket) = @_;
+sub item_note_cud {
+    my($self, $conn, $auth, $class, $note) = @_;
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
 
-	my $meth = $types{$class};
-	my $bkt = $apputils->simplereq( $svc, "$meth.retrieve", $bucket );
-	return undef unless ($bkt and $bkt->pub);
+    my $meth = 'retrieve_' . $ctypes{$class};
+    my $nclass = $note->class_name;
+    (my $iclass = $nclass) =~ s/n$//og;
 
-	$bkt->items( $apputils->simplereq( $svc,
-		"$meth"."_item.search.atomic", { bucket => $bucket } ) );
+    my $db_note = $e->$meth($note->id, {
+        flesh => 2,
+        flesh_fields => {
+            $nclass => ['item'],
+            $iclass => ['bucket']
+        }
+    });
 
-	return $bkt;
+    if($db_note->item->bucket->owner ne $e->requestor->id) {
+        return $e->die_event unless 
+            $e->allowed('UPDATE_CONTAINER', $db_note->item->bucket);
+    }
+
+    $meth = 'create_' . $ctypes{$class} if $note->isnew;
+    $meth = 'update_' . $ctypes{$class} if $note->ischanged;
+    $meth = 'delete_' . $ctypes{$class} if $note->isdeleted;
+    return $e->die_event unless $e->$meth($note);
+    $e->commit;
 }
 
 
@@ -318,21 +342,37 @@
 
 	my $stat;
 	if( $class eq 'copy' ) {
+        for my $note (@{$e->search_container_copy_bucket_item_note({item => $item->id})}) {
+            return $e->event unless 
+                $e->delete_container_copy_bucket_item_note($note);
+        }
 		return $e->event unless
 			$stat = $e->delete_container_copy_bucket_item($item);
 	}
 
 	if( $class eq 'callnumber' ) {
+        for my $note (@{$e->search_container_call_number_bucket_item_note({item => $item->id})}) {
+            return $e->event unless 
+                $e->delete_container_call_number_bucket_item_note($note);
+        }
 		return $e->event unless
 			$stat = $e->delete_container_call_number_bucket_item($item);
 	}
 
 	if( $class eq 'biblio' ) {
+        for my $note (@{$e->search_container_biblio_record_entry_bucket_item_note({item => $item->id})}) {
+            return $e->event unless 
+                $e->delete_container_biblio_record_entry_bucket_item_note($note);
+        }
 		return $e->event unless
 			$stat = $e->delete_container_biblio_record_entry_bucket_item($item);
 	}
 
 	if( $class eq 'user') {
+        for my $note (@{$e->search_container_user_bucket_item_note({item => $item->id})}) {
+            return $e->event unless 
+                $e->delete_container_user_bucket_item_note($note);
+        }
 		return $e->event unless
 			$stat = $e->delete_container_user_bucket_item($item);
 	}

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -2065,8 +2065,7 @@
 
 	$e->rollback;
 
-	#my @mbts = _make_mbts( @xacts );
-	my @mbts = $U->make_mbts( @xacts );
+	my @mbts = $U->make_mbts( $e, @xacts );
 
 	if(defined($type)) {
 		@mbts = grep { $_->xact_type eq $type } @mbts;

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -856,6 +856,14 @@
 		'open-ils.storage', $method, @params );
 }
 
+sub storagereq_xact {
+	my($self, $method, @params) = @_;
+	my $ses = $self->start_db_session();
+	my $val = $ses->request($method, @params)->gather(1);
+	$self->rollback_db_session($ses);
+    return $val;
+}
+
 sub cstorereq {
 	my( $self, $method, @params ) = @_;
 	return $self->simplereq(
@@ -1149,20 +1157,13 @@
 sub fetch_mbts {
 	my $self = shift;
 	my $id	= shift;
-	my $editor = shift || OpenILS::Utils::CStoreEditor->new;
+	my $e = shift || OpenILS::Utils::CStoreEditor->new;
+	$id = $id->id if ref($id);
+    
+    my $xact = $e->retrieve_money_billable_transaction_summary($id)
+	    or return (undef, $e->event);
 
-	$id = $id->id if (ref($id));
-
-	my $xact = $editor->retrieve_money_billable_transaction(
-		[
-			$id, {	
-				flesh => 1, 
-				flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] } 
-			}
-		]
-	) or return (undef, $editor->event);
-
-	return $self->make_mbts($xact);
+    return ($xact);
 }
 
 
@@ -1172,59 +1173,9 @@
 #--------------------------------------------------------------------
 sub make_mbts {
 	my $self = shift;
+    my $e = shift;
 	my @xacts = @_;
-
-	my @mbts;
-	for my $x (@xacts) {
-
-		my $s = new Fieldmapper::money::billable_transaction_summary;
-
-		$s->id( $x->id );
-		$s->usr( $x->usr );
-		$s->xact_start( $x->xact_start );
-		$s->xact_finish( $x->xact_finish );
-		
-		my $to = 0;
-		my $lb = undef;
-		for my $b (@{ $x->billings }) {
-			next if ($self->is_true($b->voided));
-			$to += ($b->amount * 100);
-			$lb ||= $b->billing_ts;
-			if ($b->billing_ts ge $lb) {
-				$lb = $b->billing_ts;
-				$s->last_billing_note($b->note);
-				$s->last_billing_ts($b->billing_ts);
-				$s->last_billing_type($b->billing_type);
-			}
-		}
-
-		$s->total_owed( sprintf('%0.2f', $to / 100 ) );
-		
-		my $tp = 0;
-		my $lp = undef;
-		for my $p (@{ $x->payments }) {
-			next if ($self->is_true($p->voided));
-			$tp += ($p->amount * 100);
-			$lp ||= $p->payment_ts;
-			if ($p->payment_ts ge $lp) {
-				$lp = $p->payment_ts;
-				$s->last_payment_note($p->note);
-				$s->last_payment_ts($p->payment_ts);
-				$s->last_payment_type($p->payment_type);
-			}
-		}
-
-		$s->total_paid( sprintf('%0.2f', $tp / 100 ) );
-		$s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) );
-		$s->xact_type('grocery') if ($x->grocery);
-		$s->xact_type('circulation') if ($x->circulation);
-
-		$logger->debug("Created mbts with balance_owed = ". $s->balance_owed);
-		
-		push @mbts, $s;
-	}
-		
-	return @mbts;
+    return @{$e->search_money_billable_transaction_summary({id => [ map { $_->id } @xacts ]})};
 }
 		
 		

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -197,6 +197,7 @@
         # overrides have been performed.  Go ahead and re-override.
         $circulator->override(1) if $circulator->request_precat;
         $circulator->do_permit();
+        $circulator->is_checkout(1);
         unless( $circulator->bail_out ) {
             $circulator->events([]);
             $circulator->do_checkout();
@@ -208,6 +209,7 @@
         return $data;
 
     } elsif( $api =~ /checkout/ ) {
+        $circulator->is_checkout(1);
         $circulator->do_checkout();
 
     } elsif( $api =~ /checkin/ ) {
@@ -244,6 +246,7 @@
         $circulator->do_hold_notify($circulator->notify_hold)
             if $circulator->notify_hold;
         $circulator->retarget_holds if $circulator->retarget;
+        $circulator->append_reading_list;
     }
 }
 
@@ -339,6 +342,7 @@
     volume
     title
     is_renewal
+    is_checkout
     is_noncat
     is_precat
     request_precat
@@ -1133,7 +1137,6 @@
     }
 
     if( $self->is_precat ) {
-        #$self->script_runner->insert("environment.isPrecat", 1, 1)
         $self->make_precat_copy;
         return if $self->bail_out;
 
@@ -2257,7 +2260,6 @@
 sub do_renew {
     my $self = shift;
     $self->log_me("do_renew()");
-    $self->is_renewal(1);
 
     # Make sure there is an open circ to renew that is not
     # marked as LOST, CLAIMSRETURNED, or LONGOVERDUE
@@ -2359,5 +2361,67 @@
 }
 
 
+sub append_reading_list {
+    my $self = shift;
 
+    return undef unless 
+        $self->is_checkout and 
+        $self->patron and 
+        $self->copy and 
+        !$self->is_noncat;
 
+    my $e = new_editor(xact => 1, requestor => $self->editor->requestor);
+
+    # verify history is globally enabled and uses the bucket mechanism
+    my $htype = OpenSRF::Utils::SettingsClient->new->config_value(
+        apps => 'open-ils.circ' => app_settings => 'checkout_history_mechanism');
+
+    unless($htype eq 'bucket') {
+        $e->rollback;
+        return undef;
+    }
+
+    # verify the patron wants to retain the hisory
+	my $setting = $e->search_actor_user_setting(
+		{usr => $self->patron->id, name => 'circ.keep_checkout_history'})->[0];
+    
+    unless($setting and $setting->value) {
+        $e->rollback;
+        return undef;
+    }
+
+    my $bkt = $e->search_container_copy_bucket(
+        {owner => $self->patron->id, btype => 'circ_history'})->[0];
+
+    my $pos = 1;
+
+    if($bkt) {
+        # find the next item position
+        my $last_item = $e->search_container_copy_bucket_item(
+            {bucket => $bkt->id}, {order_by => {ccbi => 'pos desc'}, limit => 1})->[0];
+        $pos = $last_item->pos + 1 if $last_item;
+
+    } else {
+        # create the history bucket if necessary
+        $bkt = Fieldmapper::container::copy_bucket->new;
+        $bkt->owner($self->patron->id);
+        $bkt->name('');
+        $bkt->btype('reading_list');
+        $bkt->pub('f');
+        $e->create_container_copy_bucket($bkt) or return $e->die_event;
+    }
+
+    my $item = Fieldmapper::container::copy_bucket_item->new;
+
+    $item->bucket($bkt->id);
+    $item->target_copy($self->copy->id);
+    $item->pos($pos);
+
+    $e->create_container_copy_bucket_item($item) or return $e->die_event;
+    $e->commit;
+
+    return undef;
+}
+
+
+

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -323,11 +323,12 @@
 NOTE
 
 sub retrieve_holds {
-	my($self, $client, $auth, $user_id) = @_;
+	my($self, $client, $auth, $user_id, $options) = @_;
 
     my $e = new_editor(authtoken=>$auth);
     return $e->event unless $e->checkauth;
     $user_id = $e->requestor->id unless defined $user_id;
+    $options ||= {};
 
     unless($user_id == $e->requestor->id) {
         my $user = $e->retrieve_actor_user($user_id) or return $e->event;
@@ -345,6 +346,21 @@
 		}, 
 		{order_by => {ahr => "request_time"}}
 	]);
+
+    if($$options{canceled}) {
+        my $count = $$options{cancel_count} || 
+            $U->ou_ancestor_setting_value($e->requestor->ws_ou, 
+                'circ.canceled_hold_display_count', $e) || 5;
+
+        my $canceled = $e->search_action_hold_request([
+		    {   usr =>  $user_id , 
+			    fulfillment_time => undef,
+			    cancel_time => {'!=' => undef},
+		    }, 
+		    {order_by => {ahr => "cancel_time desc"}, limit => $count}
+	    ]);
+        push(@$holds, @$canceled);
+    }
 	
 	if( ! $self->api_name =~ /id_list/ ) {
 		for my $hold ( @$holds ) {
@@ -438,7 +454,47 @@
 	}
 }
 
+
 __PACKAGE__->register_method(
+	method	=> "uncancel_hold",
+	api_name	=> "open-ils.circ.hold.uncancel"
+);
+
+sub uncancel_hold {
+	my($self, $client, $auth, $hold_id) = @_;
+	my $e = new_editor(authtoken=>$auth, xact=>1);
+	return $e->event unless $e->checkauth;
+
+	my $hold = $e->retrieve_action_hold_request($hold_id)
+		or return $e->die_event;
+    return $e->die_event unless $e->allowed('CANCEL_HOLDS', $hold->request_lib);
+
+    return 0 if $hold->fulfillment_time;
+    return 1 unless $hold->cancel_time;
+
+    # if configured to reset the request time, also reset the expire time
+    if($U->ou_ancestor_setting_value(
+        $hold->request_lib, 'circ.hold_reset_request_time_on_uncancel', $e)) {
+
+        $hold->request_time('now');
+        my $interval = $U->ou_ancestor_setting_value($hold->request_lib, OILS_SETTING_HOLD_EXPIRE);
+        if($interval) {
+            my $date = DateTime->now->add(seconds => OpenSRF::Utils::interval_to_seconds($interval));
+            $hold->expire_time($U->epoch2ISO8601($date->epoch));
+        }
+    }
+
+    $hold->clear_cancel_time;
+    $e->update_action_hold_request($hold) or return $e->die_event;
+    $e->commit;
+
+    $U->storagereq('open-ils.storage.action.hold_request.copy_targeter', undef, $hold_id);
+
+    return 1;
+}
+
+
+__PACKAGE__->register_method(
 	method	=> "cancel_hold",
 	api_name	=> "open-ils.circ.hold.cancel",
 	notes		=> <<"	NOTE");
@@ -449,7 +505,7 @@
 	NOTE
 
 sub cancel_hold {
-	my($self, $client, $auth, $holdid) = @_;
+	my($self, $client, $auth, $holdid, $cause, $note) = @_;
 
 	my $e = new_editor(authtoken=>$auth, xact=>1);
 	return $e->event unless $e->checkauth;
@@ -496,6 +552,8 @@
 	}
 
 	$hold->cancel_time('now');
+    $hold->cancel_cause($cause);
+    $hold->cancel_note($note);
 	$e->update_action_hold_request($hold)
 		or return $e->event;
 

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -19,6 +19,7 @@
 use OpenSRF::EX qw/:try/;
 use OpenILS::Application::AppUtils;
 use Data::Dumper;
+use OpenILS::Event;
 use Time::HiRes qw(time);
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
 
@@ -395,17 +396,52 @@
 }
 
 
+__PACKAGE__->register_method (
+	method		=> 'delete_survey',
+	api_name	=> 'open-ils.circ.survey.delete.cascade'
+);
+__PACKAGE__->register_method (
+	method		=> 'delete_survey',
+	api_name	=> 'open-ils.circ.survey.delete.cascade.override'
+);
 
+sub delete_survey {
+    my($self, $conn, $auth, $survey_id) = @_;
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
 
+    my $survey = $e->retrieve_action_survey($survey_id) 
+        or return $e->die_event;
+    return $e->die_event unless $e->allowed('ADMIN_SURVEY', $survey->owner);
 
+    my $questions = $e->search_action_survey_question({survey => $survey_id});
+    my @answers;
+    push(@answers, @{$e->search_action_survey_answer({question => $_->id})}) for @$questions;
+    my $responses = $e->search_action_survey_response({survey => $survey_id});
 
+    return OpenILS::Event->new('SURVEY_RESPONSES_EXIST')
+        if @$responses and $self->api_name =! /override/;
 
+    for my $resp (@$responses) {
+        $e->delete_action_survey_response($resp) or return $e->die_event;
+    }
 
+    for my $ans (@answers) {
+        $e->delete_action_survey_answer($ans) or return $e->die_event;
+    }
 
+    for my $quest (@$questions) {
+        $e->delete_action_survey_question($quest) or return $e->die_event;
+    }
 
-1;
+    $e->delete_action_survey($survey) or return $e->die_event;
 
+    $e->commit;
+    return 1;
+}
 
 
 
 
+
+1;

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -93,9 +93,9 @@
 __PACKAGE__->table('action_hold_request');
 __PACKAGE__->columns(Primary => 'id');
 __PACKAGE__->columns(Essential => qw/request_time capture_time fulfillment_time
-				     prev_check_time expire_time requestor usr
+				     prev_check_time expire_time requestor usr cancel_cause
 				     hold_type holdable_formats target cancel_time
-				     phone_notify email_notify selection_depth
+				     phone_notify email_notify selection_depth cancel_note
 				     pickup_lib current_copy request_lib frozen thaw_date
 				     fulfillment_staff fulfillment_lib selection_ou/);
 

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -33,10 +33,10 @@
 		if (my $old_xact = $pg->current_xact_session) {
 			if ($pg->current_xact_is_auto) {
 				$log->debug("Commiting old autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
-				$self->pg_commit_xaction($client);
+				$self->method_lookup("open-ils.storage.transaction.commit")->run();
 			} else {
 				$log->debug("Rolling back old NON-autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO);
-				$self->pg_rollback_xaction($client);
+				$self->method_lookup("open-ils.storage.transaction.rollback")->run();
 				throw OpenSRF::DomainObject::oilsException->new(
 						statusCode => 500,
 						status => "Previous transaction rolled back!",

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -890,7 +890,7 @@
 
 			if ($hold->expire_time) {
 				my $ex_time = $parser->parse_datetime( clense_ISO8601( $hold->expire_time ) );
-				$hold->update( { cancel_time => 'now' } ) if ( DateTime->compare($ex_time, DateTime->now) < 0 );
+				$hold->update( { cancel_cause => 1, cancel_time => 'now' } ) if ( DateTime->compare($ex_time, DateTime->now) < 0 );
 				$self->method_lookup('open-ils.storage.transaction.commit')->run;
 			}
 

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -484,10 +484,10 @@
 	# group 2 = phone, ident
 	# group 3 = barcode
 
-	my $usr = join ' AND ', map { "LOWER($_) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
+	my $usr = join ' AND ', map { "LOWER(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
 	my @usrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
 
-	my $addr = join ' AND ', map { "LOWER($_) ~ ?" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
+	my $addr = join ' AND ', map { "LOWER(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
 	my @addrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
 
 	my $pv = $$search{phone}{value};
@@ -563,8 +563,8 @@
 
 	return undef if (!$select && !$card);
 
-	my $order_by = join ', ', map { 'LOWER(users.'. (split / /,$_)[0] . ') ' . (split / /,$_)[1] } @$sort;
-	my $distinct_list = join ', ', map { 'LOWER(users.'. (split / /,$_)[0] . ')' } @$sort;
+	my $order_by = join ', ', map { 'LOWER(CAST(users.'. (split / /,$_)[0] . ' AS text)) ' . (split / /,$_)[1] } @$sort;
+	my $distinct_list = join ', ', map { 'LOWER(CAST(users.'. (split / /,$_)[0] . ' AS text))' } @$sort;
 
 	if ($inactive) {
 		$inactive = '';

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -12,10 +12,16 @@
 sub new {
     my $class = shift;
     my $id = shift;
+    my $editor = shift;
     $class = ref($class) || $class;
 
-    my $self = bless { id => $id, editor => new_editor() } => $class;
+    return $id if (ref($id) && ref($id) == $class);
 
+    my $standalone = $editor ? 0 : 1;
+    $editor ||= new_editor();
+
+    my $self = bless { id => $id, editor => $editor, standalone => $standalone } => $class;
+
     return $self->init()
 }
 
@@ -35,16 +41,41 @@
             $self->id, {
                 flesh => 2,
                 flesh_fields => {
-                    atev    => [ 'event_def' ],
-                    atevdef => [ 'hook' ]
+                    atev    => [ qw/event_def/ ],
+                    atevdef => [ qw/hook env params/ ]
                 }
             }
         ])
     );
 
+    if ($self->event->state eq 'valid') {
+        $self->valid(1);
+    } elsif ($self->event->state eq 'invalid') {
+        $self->valid(0);
+    } elsif ($self->event->state eq 'reacting') {
+        $self->valid(1);
+    } elsif ($self->event->state eq 'reacted') {
+        $self->valid(1);
+        $self->reacted(1);
+    } elsif ($self->event->state eq 'cleaning') {
+        $self->valid(1);
+        $self->reacted(1);
+    } elsif ($self->event->state eq 'complete') {
+        $self->valid(1);
+        $self->reacted(1);
+        $self->cleanedup(1);
+    } elsif ($self->event->state eq 'error') {
+        $self->valid(0);
+        $self->reacted(0);
+        $self->cleanedup(0);
+    }
+
+
+    $self->update_state('found') || die 'Unable to update event state';
+
     my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type );
     
-    my $meth = "retreive_" . $class;
+    my $meth = "retrieve_" . $class;
     $meth =~ s/Fieldmapper:://;
     $meth =~ s/::/_/;
     
@@ -55,14 +86,17 @@
 
 sub cleanup {
     my $self = shift;
+    my $env = shift || $self->environment;
 
+    return $self if (defined $self->cleanedup);
+
     if (defined $self->reacted) {
         $self->update_state( 'cleaning') || die 'Unable to update event state';
         try {
             my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure;
             $self->cleanedup(
                 OpenILS::Application::Trigger::ModRunner::Cleanup
-                    ->new( $cleanup, $self->environment )
+                    ->new( $cleanup, $env)
                     ->run
                     ->final_result
             );
@@ -85,7 +119,10 @@
 
 sub react {
     my $self = shift;
+    my $env = shift || $self->environment;
 
+    return $self if (defined $self->reacted);
+
     if ($self->valid) {
         if ($self->event->event_def->group_field) { # can't react individually to a grouped definition
             $self->{reacted} = undef;
@@ -94,7 +131,7 @@
             try {
                 $self->reacted(
                     OpenILS::Application::Trigger::ModRunner::Reactor
-                        ->new( $self->event->event_def->reactor, $self->environment )
+                        ->new( $self->event->event_def->reactor, $env )
                         ->run
                         ->final_result
                 );
@@ -213,6 +250,19 @@
     return $self->{editor};
 }
 
+sub unfind {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    die 'Cannot unfind a reacted event' if (defined $self->reacted);
+
+    $self->update_state( 'pending' ) || die 'Unable to update event state';
+    $self->{id} = undef;
+    $self->{event} = undef;
+    $self->{environment} = undef;
+    return $self;
+}
+
 sub target {
     my $self = shift;
     return undef unless (ref $self);
@@ -222,6 +272,15 @@
     return $self->{target};
 }
 
+sub standalone {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    my $t = shift;
+    $self->{standalone} = $t if (defined $t);
+    return $self->{standalone};
+}
+
 sub update_state {
     my $self = shift;
     return undef unless ($self && ref $self);
@@ -229,15 +288,35 @@
     my $state = shift;
     return undef unless ($state);
 
-    $self->editor->xact_begin || return undef;
+    if ($self->standalone) {
+        $self->editor->xact_begin || return undef;
+    }
 
     my $e = $self->editor->retrieve_action_trigger_event( $self->id );
+    $e->start_time( 'now' ) unless $e->start_time;
     $e->update_time( 'now' );
     $e->update_process( $$ );
     $e->state( $state );
-    $self->editor->update_action_trigger_event( $e );
 
-    return $self->editor->xact_commit || undef;
+    $e->clear_start_time() if ($e->state eq 'pending');
+
+    my $ok = $self->editor->update_action_trigger_event( $e );
+    if (!$ok) {
+        $self->editor->xact_rollback if ($self->standalone);
+        return undef;
+    } else {
+        $ok = $self->editor->xact_commit if ($self->standalone);
+    }
+
+    if ($ok) {
+        $e = $self->editor->data;
+        $self->event->start_time( $e->start_time );
+        $self->event->update_time( $e->update_time );
+        $self->event->update_process( $e->update_process );
+        $self->event->state( $e->state );
+    }
+
+    return $ok || undef;
 }
 
 sub build_environment {
@@ -251,19 +330,21 @@
         $self->environment->{target} = $self->target;
         $self->environment->{event} = $self->event;
         $self->environment->{template} = $self->event->event_def->template;
+
+        $self->environment->{params}{ $_->param } = eval $_->value for ( @{$self->event->event_def->params} );
     
-        my @env_list = $self->editor->search_action_trigger_environment( { event_def => $self->event->event_def } );
-        my @param_list = $self->editor->search_action_trigger_params( { event_def => $self->event->event_def } );
-    
-        $self->environment->{params}{ $_->param } = eval $_->value for ( @param_list );
-    
-        for my $e ( @env_list ) {
+        for my $e ( @{$self->event->event_def->env} ) {
             my (@label, @path);
             @path = split('.', $e->path) if ($e->path);
             @label = split('.', $e->label) if ($e->label);
     
             $self->_object_by_path( $self->event->target, $e->collector, \@label, \@path );
         }
+
+        if ($self->event->event_def->group_field) {
+            my @group_path = split('.', $self->event->event_def->group_field);
+            my $group_object = $self->_object_by_path( $self->event->target, undef, [], \@group_path );
+        }
     
         $self->environment->{complete} = 1;
     } otherwise {

Copied: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm (from rev 12227, trunk/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm)
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm	                        (rev 0)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -0,0 +1,242 @@
+package OpenILS::Application::Trigger::EventGroup;
+use OpenILS::Application::Trigger::Event;
+use base 'OpenILS::Application::Trigger::Event';
+use OpenSRF::EX qw/:try/;
+
+use OpenSRF::Utils::Logger qw/:level/;
+
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenILS::Application::Trigger::ModRunner;
+
+my $log = 'OpenSRF::Utils::Logger';
+
+sub new {
+    my $class = shift;
+    my @ids = @_;
+    $class = ref($class) || $class;
+
+    my $editor = new_editor(xact=>1);
+
+    my $self = bless {
+        environment => {},
+        events      => [
+            map {
+                ref($_) ?
+                    do { $_->standalone(0); $_->editor($editor); $_ } :
+                    OpenILS::Application::Trigger::Event->new($_, $editor)
+            } @ids
+        ],
+        ids         => \@ids,
+        editor      => $editor
+    } => $class;
+
+
+    $self->editor->xact_commit; # flush out those updates
+    $self->editor->xact_begin;
+
+    return $self;
+}
+
+sub react {
+    my $self = shift;
+
+    return $self if (defined $self->reacted);
+
+    if ($self->valid) {
+        $self->update_state( 'reacting') || die 'Unable to update event group state';
+        $self->build_environment;
+
+        try {
+            $self->reacted(
+                OpenILS::Application::Trigger::ModRunner::Reactor
+                    ->new( $self->event->event_def->reactor, $self->environment )
+                    ->run
+                    ->final_result
+            );
+        } otherwise {
+            $log->error( shift() );
+            $self->update_state( 'error' ) || die 'Unable to update event group state';
+        };
+
+        if (defined $self->reacted) {
+            $self->update_state( 'reacted' ) || die 'Unable to update event group state';
+        } else {
+            $self->update_state( 'error' ) || die 'Unable to update event group state';
+        }
+    } else {
+        $self->{reacted} = undef;
+    }
+    return $self;
+}
+
+sub validate {
+    my $self = shift;
+
+    return $self if (defined $self->valid);
+
+    $self->update_state( 'validating') || die 'Unable to update event group state';
+    $self->editor->xact_begin;
+
+    my @valid_events;
+    try {
+        for my $event ( @{ $self->events } ) {
+            $event->validate;
+            push @valid_events, $event if ($event->valid);
+        }
+        $self->valid(1) if (@valid_events);
+        $self->{events} = \@valid_events;
+        $self->editor->xact_commit;
+    } otherwise {
+        $log->error( shift() );
+        $self->editor->xact_rollback;
+        $self->update_state( 'error' ) || die 'Unable to update event group state';
+    };
+
+    return $self;
+}
+ 
+sub cleanedup {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    my $c = shift;
+    $self->{cleanedup} = $c if (defined $c);
+    return $self->{cleanedup};
+}
+
+sub reacted {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    my $r = shift;
+    $self->{reacted} = $r if (defined $r);
+    return $self->{reacted};
+}
+
+sub valid {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    my $v = shift;
+    $self->{valid} = $v if (defined $v);
+    return $self->{valid};
+}
+
+sub event {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    return $self->{events}[0];
+}
+
+sub events {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    return $self->{events};
+}
+
+sub ids {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    return $self->{ids};
+}
+
+sub environment {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    my $e = shift;
+    $self->{environment} = $e if (defined $e);
+    return $self->{environment};
+}
+
+sub editor {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    my $e = shift;
+    $self->{editor} = $e if (defined $e);
+    return $self->{editor};
+}
+
+sub unfind {
+    my $self = shift;
+    return undef unless (ref $self);
+
+    die 'Cannot unfind a reacted event group' if (defined $self->reacted);
+
+    $self->update_state( 'pending' ) || die 'Unable to update event group state';
+    $self->{events} = undef;
+    return $self;
+}
+
+sub update_state {
+    my $self = shift;
+    return undef unless ($self && ref $self);
+
+    my $state = shift;
+    return undef unless ($state);
+
+    $self->editor->xact_begin || return undef;
+
+    my @oks;
+    for my $event ( @{ $self->events } ) {
+        my $e = $self->editor->retrieve_action_trigger_event( $event->id );
+        $e->start_time( 'now' ) unless $e->start_time;
+        $e->update_time( 'now' );
+        $e->update_process( $$ );
+        $e->state( $state );
+    
+        $e->clear_start_time() if ($e->state eq 'pending');
+    
+        my $ok = $self->editor->update_action_trigger_event( $e );
+        if ($ok) {
+            push @oks, $ok;
+        }
+    }
+
+    if (scalar(@oks) < scalar(@{ $self->ids })) {
+        $self->editor->xact_rollback;
+        return undef;
+    } else {
+        $ok = $self->editor->xact_commit;
+    }
+
+    if ($ok) {
+        for my $event ( @{ $self->events } ) {
+            my $updated = $self->editor->data;
+            $event->start_time( $updated->start_time );
+            $event->update_time( $updated->update_time );
+            $event->update_process( $updated->update_process );
+            $event->state( $updated->state );
+        }
+    }
+
+    return $ok || undef;
+}
+
+sub build_environment {
+    my $self = shift;
+    my $env = $self->environment;
+
+    $$evn{target} = [];
+    $$evn{event} = [];
+    for my $e ( @{ $self->events } ) {
+        for my $evn_part ( keys %{ $e->environment } ) {
+            if ($env_part eq 'target') {
+                push @{ $$evn{target} }, $e->environment->{target};
+            } elsif ($env_part eq 'event') {
+                push @{ $$evn{event} }, $e->environment->{event};
+            } else {
+                $$evn{$evn_part} = $e->environment->{$evn_part};
+            }
+        }
+    }
+
+    return $self;
+}
+
+1;

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -7,10 +7,15 @@
 use OpenSRF::AppSession;
 use OpenSRF::Utils::SettingsClient;
 use OpenSRF::Utils::Logger qw/:level/;
+use OpenSRF::Utils qw/:datetime/;
 
+use DateTime;
+use DateTime::Format::ISO8601;
+
 use OpenILS::Utils::Fieldmapper;
 use OpenILS::Utils::CStoreEditor q/:funcs/;
 use OpenILS::Application::Trigger::Event;
+use OpenILS::Application::Trigger::EventGroup;
 
 
 my $log = 'OpenSRF::Utils::Logger';
@@ -18,4 +23,219 @@
 sub initialize {}
 sub child_init {}
 
+sub create_events_for_object {
+    my $self = shift;
+    my $client = shift;
+    my $key = shift;
+    my $target = shift;
+    my $location = shift;
+
+    my $ident = $target->Identity;
+    my $ident_value = $target->$ident();
+
+    my $editor = new_editor(xact=>1);
+
+    my $hooks = $editor->search_action_trigger_hook(
+        { key       => $key,
+          core_type => $target->json_hint
+        }
+    );
+
+    my %hook_hash = map { ($_->id, $_) } @$hooks;
+
+    my $orgs = $editor->json_query({ from => [ 'actor.org_unit_ancestors' => $location ] });
+    my $defs = $editor->search_action_trigger_event_definition([
+        { hook   => [ keys %hook_hash ],
+          owner  => [ map { $_->{id} } @$orgs  ],
+          active => 't'
+        },
+        { idlist => 1 }
+    ]);
+
+    for my $def ( @$defs ) {
+
+        my $date = DateTime->now;
+
+        if ($hook_hash{$def->hook}->passive eq 'f') {
+
+            if (my $dfield = $def->delay_field) {
+                if ($target->$dfield()) {
+                    $date = DateTime::Format::ISO8601->new->parse_datetime( clense_ISO8601($target->$dfield) );
+                } else {
+                    next;
+                }
+            }
+
+            $date->add( seconds => interval_to_seconds($def->delay) );
+        }
+
+        my $event = Fieldmapper::action_trigger::event->new();
+        $event->target( $ident_value );
+        $event->event_def( $def->id );
+        $event->run_time( $date->strftime( '%G %T%z' ) );
+
+        $event = $editor->create_action_trigger_event( $event );
+
+        $client->respond( $event->id );
+    }
+
+    $editor->commit;
+
+    return undef;
+}
+__PACKAGE__->register_method(
+    api_name => 'open-ils.trigger.event.autocreate',
+    method   => 'create_events_for_object',
+    api_level=> 1,
+    stream   => 1,
+    argc     => 3
+);
+
+
+sub fire_single_event {
+    my $self = shift;
+    my $client = shift;
+    my $event_id = shift;
+
+    my $e = OpenILS::Application::Trigger::Event->new($event_id);
+
+    if ($e->validate->valid) {
+        $e->react->cleanup;
+    }
+
+    return {
+        valid     => $e->valid,
+        reacted   => $e->reacted,
+        cleanedup => $e->cleanedup,
+        event     => $e->event
+    };
+}
+__PACKAGE__->register_method(
+    api_name => 'open-ils.trigger.event.fire',
+    method   => 'fire_single_event',
+    api_level=> 1,
+    argc     => 1
+);
+
+sub fire_event_group {
+    my $self = shift;
+    my $client = shift;
+    my $events = shift;
+
+    my $e = OpenILS::Application::Trigger::EventGroup->new(@$events);
+
+    if ($e->validate->valid) {
+        $e->react->cleanup;
+    }
+
+    return {
+        valid     => $e->valid,
+        reacted   => $e->reacted,
+        cleanedup => $e->cleanedup,
+        events    => $e->events
+    };
+}
+__PACKAGE__->register_method(
+    api_name => 'open-ils.trigger.event_group.fire',
+    method   => 'fire_event_group',
+    api_level=> 1,
+    argc     => 1
+);
+
+sub pending_events {
+    my $self = shift;
+    my $client = shift;
+
+    my $editor = new_editor();
+
+    return $editor->search_action_trigger_event([
+        { state => 'pending', run_time => {'<' => 'now'} },
+        { idlist=> 1 }
+    ]);
+}
+__PACKAGE__->register_method(
+    api_name => 'open-ils.trigger.event.find_pending',
+    method   => 'pending_events',
+    api_level=> 1
+);
+
+
+sub grouped_events {
+    my $self = shift;
+    my $client = shift;
+
+    my ($events) = $self->method_lookup('open-ils.trigger.event.find_pending')->run();
+
+    my %groups = ( '*' => [] );
+
+    for my $e_id ( @$events ) {
+        my $e = OpenILS::Application::Trigger::Event->new($e_id);
+        if ($e->validate->valid) {
+            if (my $group = $event->event->event_def->group_field) {
+
+                # split the grouping link steps
+                my @steps = split '.', $group;
+
+                # find the grouping object
+                my $node = $event->target;
+                $node = $node->$_() for ( @steps );
+
+                # get the pkey value for the grouping object on this event
+                my $node_ident = $node->Identity;
+                my $ident_value = $node->$node_ident();
+
+                # push this event onto the event+grouping_pkey_value stack
+                $groups{$e->event->event_def->id}{$ident_value} ||= [];
+                push @{ $groups{$e->event->event_def->id}{$ident_value} }, $e;
+            } else {
+                # it's a non-grouped event
+                push @{ $groups{'*'} }, $e;
+            }
+        }
+    }
+
+    return \%groups;
+}
+__PACKAGE__->register_method(
+    api_name => 'open-ils.trigger.event.find_pending_by_group',
+    method   => 'grouped_events',
+    api_level=> 1
+);
+
+sub run_all_events {
+    my $self = shift;
+    my $client = shift;
+
+    my ($groups) = $self->method_lookup('open-ils.trigger.event.find_pending_by_group')->run();
+
+    for my $def ( %$groups ) {
+        if ($def eq '*') {
+            for my $event ( @{ $$groups{'*'} } ) {
+                $client->respond(
+                    $self
+                        ->method_lookup('open-ils.trigger.event.fire')
+                        ->run($event)
+                );
+            }
+        } else {
+            my $defgroup = $$groups{$def};
+            for my $ident ( keys %$defgroup ) {
+                $client->respond(
+                    $self
+                        ->method_lookup('open-ils.trigger.event_group.fire')
+                        ->run($$defgroup{$ident})
+                );
+            }
+        }
+    }
+                
+            
+}
+__PACKAGE__->register_method(
+    api_name => 'open-ils.trigger.event.run_all_pending',
+    method   => 'run_all_events',
+    api_level=> 1
+);
+
+
 1;

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -59,6 +59,7 @@
 		$$fieldmap{$n}{hint} = $c;
 		$$fieldmap{$n}{virtual} = ($idl->{$c}{'oils_persist:virtual'} && $idl->{$c}{'oils_persist:virtual'} eq 'true') ? 1 : 0;
 		$$fieldmap{$n}{table} = $idl->{$c}{'oils_persist:tablename'};
+		$$fieldmap{$n}{restrict_primary} = $idl->{$c}{'oils_persist:restrict_primary'};
 		$$fieldmap{$n}{sequence} = $idl->{$c}{fields}{'oils_persist:sequence'};
 		$$fieldmap{$n}{identity} = $idl->{$c}{fields}{'oils_persist:primary'};
 
@@ -176,6 +177,11 @@
 	return $$fieldmap{$self->class_name}{identity};
 }
 
+sub RestrictPrimary {
+	my $self = shift;
+	return $$fieldmap{$self->class_name}{restrict_primary};
+}
+
 sub Sequence {
 	my $self = shift;
 	return $$fieldmap{$self->class_name}{sequence};

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -3,6 +3,7 @@
 use integer;
 use Carp;
 
+use DateTime;
 use MARC::Record;
 
 sub new
@@ -97,6 +98,8 @@
 sub decode_pattern {
     my $self = shift;
     my $pattern = $self->{PATTERN}->{y};
+
+    # XXX WRITE ME (?)
 }
 
 sub compressible {
@@ -149,4 +152,215 @@
     return (exists $self->{PATTERN}->{w} && exists $self->{PATTERN}->{y});
 }
 
+my %daynames = (
+		'mo' => 1,
+		'tu' => 2,
+		'we' => 3,
+		'th' => 4,
+		'fr' => 5,
+		'sa' => 6,
+		'su' => 7,
+	       );
+
+my $daypat = '(mo|tu|we|th|fr|sa|su)';
+my $weekpat = '(99|98|97|00|01|02|03|04|05)';
+my $weeknopat;
+my $monthpat = '(01|02|03|04|05|06|07|08|09|10|11|12)';
+my $seasonpat = '(21|22|23|24)';
+
+# Initialize $weeknopat to be '(01|02|03|...|51|52|53)'
+$weeknopat = '(';
+foreach my $weekno (1..52) {
+    $weeknopat .= sprintf("%02d|", $weekno);
+}
+$weeknopat .= '53)';
+
+sub match_day {
+    my $pat = shift;
+    my @date = @_;
+    # Translate daynames into day of week for DateTime
+    # also used to check if dayname is valid.
+
+    if (exists $daynames{$pat}) {
+	# dd
+	# figure out day of week for date and compare
+	my $dt = DateTime->new(year  => $date[0],
+			       month => $date[1],
+			       day   => $date[2]);
+	return ($dt->day_of_week == $daynames{$pat});
+    } elsif (length($pat) == 2) {
+	# MM
+	return $pat == $date[3];
+    } elsif (length($pat) == 4) {
+	# MMDD
+	my ($mon, $day);
+	$mon = substr($pat, 0, 2);
+	$day = substr($pat, 2, 2);
+
+	return (($mon == $date[1]) && ($day == $date[2]));
+    } else {
+	carp "Invalid day pattern '$pat'";
+	return 0;
+    }
+}
+
+# Calcuate date of "n"th last "dayname" of month: second last Tuesday
+sub last_week_of_month {
+    my $dt = shift;
+    my $week = shift;
+    my $day = shift;
+    my $end_dt = DateTime->last_day_of_month(year  => $dt->year,
+					     month => $dt->month);
+
+    $day = $daynames{$day};
+    while ($end_dt->day_of_week != $day) {
+	$end_dt->subtract(days => 1);
+    }
+
+    # 99: last week of month, 98: second last, etc.
+    for (my $i = 99 - $week; $i > 0; $i--) {
+	$end_dt->subtract(weeks => 1);
+    }
+
+    return $end_dt;
+}
+
+sub check_date {
+    my $dt = shift;
+    my $month = shift;
+    my $weekno = shift;
+    my $day = shift;
+
+    if (!defined $day) {
+	# MMWW
+	return (($dt->month == $month)
+		&& (($dt->week_of_month == $weekno)
+		    || ($dt->week_of_month == last_day_of_month($dt, $weekno, 'th')->week_of_month)));
+    }
+
+    # simple cases first
+    if ($daynames{$day} != $dt->day_of_week) {
+	# if it's the wrong day of the week, rest doesn't matter
+	return 0;
+    }
+
+    if (!defined $month) {
+	# WWdd
+	return (($dt->weekday_of_month == $weekno)
+		|| ($dt->weekday_of_month == last_day_of_month($dt, $weekno, $day)->weekday_of_month));
+    }
+
+    # MMWWdd
+    if ($month != $dt->month) {
+	# If it's the wrong month, then we're done
+	return 0;
+    }
+
+    # It's the right day of the week
+    # It's the right month
+
+    if ($weekno == $dt->weekday_of_month) {
+	# If this matches, then we're counting from the beginning
+	# of the month and it matches and we're done.
+	return 1;
+    }
+
+    # only case left is that the week number is counting from
+    # the end of the month: eg, second last wednesday
+    return (last_week_of_month($weekno, $day)->weekday_of_month == $dt->weekday_of_month);
+}
+
+sub match_week {
+    my $pat = shift;
+    my @date = @_;
+    my $dt = DateTime->new(year  => $date[0],
+			   month => $date[1],
+			   day   => $date[2]);
+
+    if ($pat =~ m/^$weekpat$daypat$/) {
+	# WWdd: 03we = Third Wednesday
+	return check_date($dt, undef, $1, $2);
+    } elsif ($pat =~ m/^$monthpat$weekpat$daypat$/) {
+	# MMWWdd: 0599tu Last Tuesday in May XXX WRITE ME
+	return check_date($dt, $1, $2, $3);
+    } elsif ($pat =~ m/^$monthpat$weekpat$/) {
+	# MMWW: 1204: Fourth week in December XXX WRITE ME
+	return check_date($dt, $1, $2, undef);
+    } else {
+	carp "invalid week pattern '$pat'";
+	return 0;
+    }
+}
+
+sub match_month {
+    my $pat = shift;
+    my @date = @_;
+
+    return ($pat eq $date[1]);
+}
+
+sub match_season {
+    my $pat = shift;
+    my @date = @_;
+
+    return ($pat eq $date[1]);
+}
+
+sub match_year {
+    my $pat = shift;
+    my @date = @_;
+
+    # XXX WRITE ME
+}
+
+my %dispatch = (
+		'd' => \&match_day,
+		'w' => \&match_week,
+		'm' => \&match_month,
+		's' => \&match_season,
+		'y' => \&match_year,
+);
+sub regularity_match {
+    my $self = shift;
+    my $pubcode = shift;
+    my @date = @_;
+
+    foreach my $regularity ($self->{PATTERN}->{y}) {
+	next unless $regularity =~ m/^$pubcode/;
+
+	my $chroncode= substr($regularity, 1, 1);
+	my @pats = split(/,/, substr($regularity, 2));
+
+	# XXX WRITE ME
+	foreach my $pat (@pats) {
+	    if ($dispatch{$chroncode}->($pat, @date)) {
+		return 1;
+	    }
+	}
+    }
+
+    return 0;
+}
+
+sub is_omitted {
+    my $self = shift;
+    my @date = @_;
+
+    return $self->regularity_match('o', @date);
+}
+
+sub is_published {
+    my $self = shift;
+    my @date = @_;
+
+    return $self->regularity_match('p', @date);
+}
+
+sub is_combined {
+    my $self = shift;
+    my @date = @_;
+
+    return $self->regularity_match('c', @date);
+}
+
 1;

Modified: branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm	2009-02-19 05:39:53 UTC (rev 12228)
@@ -197,36 +197,20 @@
 		  # x => completely irregular
 };
 
-sub next_date {
-    my $self = shift;
-    my $next = shift;
-    my @keys = @_;
-    my @cur;
-    my @new;
-    my $incr;
+sub is_combined {
+    my $str = shift;
 
-    my $caption = $self->{CAPTION};
-    my $pattern = $caption->{PATTERN};
-    my $frequency = $pattern->{w};
+    return $str =~ m;.+/.+;
+}
 
-    warn "I can't deal with publication patterns yet!" if exists $pattern->{y};
+sub incr_date {
+    my $incr = shift;
+    my @new = @_;
 
-#     print Dumper(@keys);
-#     print Dumper($self);
-
-    foreach my $i (0.. at keys) {
-	$new[$i] = $cur[$i] = $self->{SUBFIELDS}->{$keys[$i]}
-	  if exists $self->{SUBFIELDS}->{$keys[$i]};
-    }
-
-    if (defined $frequency) {
-	$incr = $increments{$frequency};
-    }
-
-    if (scalar(@cur) == 1) {
+    if (scalar(@new) == 1) {
 	# only a year is specified. Next date is easy
 	$new[0] += $incr->{years} || 1;
-    } elsif (scalar(@cur) == 2) {
+    } elsif (scalar(@new) == 2) {
 	# Year and month or season
 	if ($new[1] > 20) {
 	    # season
@@ -245,7 +229,7 @@
 		$new[1] -= 12;
 	    }
 	}
-    } elsif (scalar(@cur) == 3) {
+    } elsif (scalar(@new) == 3) {
 	# Year, Month, Day: now it gets complicated.
 
 	if ($new[2] =~ /^[0-9]+$/) {
@@ -257,24 +241,51 @@
 	    $new[0] = $dt->year;
 	    $new[1] = $dt->month;
 	    $new[2] = $dt->day;
-	} elsif ($new[2] =~ /^([0-9]+)\/([0-9]+)/) {
-	    my $sdt = DateTime->new(year => $new[0],
-				    month=> $new[1],
-				    day  => $1);
-	    my $edt = DateTime->new(year => $new[0],
-				    month=> $new[1],
-				    day  => $2);
-	    $sdt->add(%{$incr});
-	    $edt->add(%{$incr});
-	    $new[0] = $sdt->year;
-	    $new[1] = $sdt->month;
-	    $new[2] = $sdt->day . '/' . $edt->day;
-	} else {
-	    warn "I don't know how to deal with '$new[2]'";
 	}
+    } else {
+	warn("Don't know how to cope with @new");
     }
+
+    return @new;
 }
 
+sub next_date {
+    my $self = shift;
+    my $next = shift;
+    my @keys = @_;
+    my @cur;
+    my @new;
+    my $incr;
+
+    my $caption = $self->{CAPTION};
+    my $reg = $caption->{REGULARITY};
+    my $pattern = $caption->{PATTERN};
+    my $freq = $pattern->{w};
+
+    foreach my $i (0.. at keys) {
+	$new[$i] = $cur[$i] = $self->{SUBFIELDS}->{$keys[$i]}
+	  if exists $self->{SUBFIELDS}->{$keys[$i]};
+    }
+
+    if (is_combined($new[-1])) {
+	$new[-1] =~ s/^[^\/]+//;
+    }
+
+    # If $frequency is not one of the standard codes defined in %increments
+    # then there has to be a $yp publication regularity pattern that
+    # lists the dates of publication. Use that that list to find the next
+    # date following the current one.
+    # XXX: the code doesn't handle this case yet.
+
+    if (defined $freq && exists $increments{$freq}) {
+	@new = incr_date($increments{$freq}, @new);
+
+	while ($caption->is_omitted(@new)) {
+	    @new = incr_date($increments{$freq}, @new);
+	}
+    }
+}
+
 sub next_alt_enum {
     my $self = shift;
     my $next = shift;

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/002.schema.config.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/002.schema.config.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/002.schema.config.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -476,7 +476,7 @@
 
 CREATE TABLE config.z3950_attr (
     id          SERIAL  PRIMARY KEY,
-    source      TEXT    NOT NULL REFERENCES config.z3950_source (name),
+    source      TEXT    NOT NULL REFERENCES config.z3950_source (name) DEFERRABLE INITIALLY DEFERRED,
     name        TEXT    NOT NULL,
     label       TEXT    NOT NULL,
     code        INT     NOT NULL,

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/005.schema.actors.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/005.schema.actors.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/005.schema.actors.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -456,6 +456,7 @@
 	state			TEXT	NOT NULL,
 	country			TEXT	NOT NULL,
 	post_code		TEXT	NOT NULL,
+    pending         BOOL    NOT NULL DEFAULT FALSE,
 	replaces	    INT	REFERENCES actor.usr_address (id) DEFERRABLE INITIALLY DEFERRED
 );
 

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/012.schema.vandelay.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/012.schema.vandelay.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -99,8 +99,8 @@
 -- DROP TABLE vandelay.import_item CASCADE;
 CREATE TABLE vandelay.import_item (
     id              BIGSERIAL   PRIMARY KEY,
-    record          BIGINT      NOT NULL REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE,
-    definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE,
+    record          BIGINT      NOT NULL REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    definition      BIGINT      NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     owning_lib      INT,
     circ_lib        INT,
     call_number     TEXT,

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/040.schema.asset.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/040.schema.asset.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/040.schema.asset.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -69,8 +69,8 @@
 CREATE TABLE asset.copy_transparency (
 	id		SERIAL		PRIMARY KEY,
 	deposit_amount	NUMERIC(6,2),
-	owner		INT		NOT NULL REFERENCES actor.org_unit (id),
-	circ_lib	INT		REFERENCES actor.org_unit (id),
+	owner		INT		NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
+	circ_lib	INT		REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
 	loan_duration	INT		CHECK ( loan_duration IN (1,2,3) ),
 	fine_level	INT		CHECK ( fine_level IN (1,2,3) ),
 	holdable	BOOL,
@@ -86,8 +86,8 @@
 
 CREATE TABLE asset.copy_tranparency_map (
 	id		BIGSERIAL	PRIMARY KEY,
-	tansparency	INT	NOT NULL REFERENCES asset.copy_transparency (id),
-	target_copy	INT	NOT NULL UNIQUE REFERENCES asset.copy (id)
+	tansparency	INT	NOT NULL REFERENCES asset.copy_transparency (id) DEFERRABLE INITIALLY DEFERRED,
+	target_copy	INT	NOT NULL UNIQUE REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED
 );
 CREATE INDEX cp_tr_cp_idx ON asset.copy_tranparency_map (tansparency);
 

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/070.schema.container.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/070.schema.container.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/070.schema.container.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -34,12 +34,18 @@
 								DEFERRABLE
 								INITIALLY DEFERRED,
 	name		TEXT				NOT NULL,
-	btype		TEXT				NOT NULL DEFAULT 'misc' REFERENCES container.copy_bucket_type (code),
+	btype		TEXT				NOT NULL DEFAULT 'misc' REFERENCES container.copy_bucket_type (code) DEFERRABLE INITIALLY DEFERRED,
 	pub		BOOL				NOT NULL DEFAULT FALSE,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	CONSTRAINT cb_name_once_per_owner UNIQUE (owner,name,btype)
 );
 
+CREATE TABLE container.copy_bucket_note (
+    id      SERIAL      PRIMARY KEY,
+    bucket  INT         NOT NULL REFERENCES container.copy_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
+
 CREATE TABLE container.copy_bucket_item (
 	id		SERIAL	PRIMARY KEY,
 	bucket		INT	NOT NULL
@@ -54,9 +60,15 @@
 					ON UPDATE CASCADE
 					DEFERRABLE
 					INITIALLY DEFERRED,
+    pos         INT,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW()
 );
 
+CREATE TABLE container.copy_bucket_item_note (
+    id      SERIAL      PRIMARY KEY,
+    item    INT         NOT NULL REFERENCES container.copy_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
 
 
 
@@ -74,12 +86,18 @@
 				DEFERRABLE
 				INITIALLY DEFERRED,
 	name	TEXT	NOT NULL,
-	btype	TEXT	NOT NULL DEFAULT 'misc' REFERENCES container.call_number_bucket_type (code),
+	btype	TEXT	NOT NULL DEFAULT 'misc' REFERENCES container.call_number_bucket_type (code) DEFERRABLE INITIALLY DEFERRED,
 	pub	BOOL	NOT NULL DEFAULT FALSE,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	CONSTRAINT cnb_name_once_per_owner UNIQUE (owner,name,btype)
 );
 
+CREATE TABLE container.call_number_bucket_note (
+    id      SERIAL      PRIMARY KEY,
+    bucket  INT         NOT NULL REFERENCES container.call_number_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
+
 CREATE TABLE container.call_number_bucket_item (
 	id		SERIAL	PRIMARY KEY,
 	bucket		INT	NOT NULL
@@ -94,11 +112,19 @@
 					ON UPDATE CASCADE
 					DEFERRABLE
 					INITIALLY DEFERRED,
+    pos         INT,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW()
 );
 
+CREATE TABLE container.call_number_bucket_item_note (
+    id      SERIAL      PRIMARY KEY,
+    item    INT         NOT NULL REFERENCES container.call_number_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
 
 
+
+
 CREATE TABLE container.biblio_record_entry_bucket_type (
 	code	TEXT	PRIMARY KEY,
 	label	TEXT	NOT NULL UNIQUE
@@ -114,12 +140,18 @@
 				DEFERRABLE
 				INITIALLY DEFERRED,
 	name	TEXT	NOT NULL,
-	btype	TEXT	NOT NULL DEFAULT 'misc' REFERENCES container.biblio_record_entry_bucket_type (code),
+	btype	TEXT	NOT NULL DEFAULT 'misc' REFERENCES container.biblio_record_entry_bucket_type (code) DEFERRABLE INITIALLY DEFERRED,
 	pub	BOOL	NOT NULL DEFAULT FALSE,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	CONSTRAINT breb_name_once_per_owner UNIQUE (owner,name,btype)
 );
 
+CREATE TABLE container.biblio_record_entry_bucket_note (
+    id      SERIAL      PRIMARY KEY,
+    bucket  INT         NOT NULL REFERENCES container.biblio_record_entry_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
+
 CREATE TABLE container.biblio_record_entry_bucket_item (
 	id				SERIAL	PRIMARY KEY,
 	bucket				INT	NOT NULL
@@ -134,11 +166,18 @@
 							ON UPDATE CASCADE
 							DEFERRABLE
 							INITIALLY DEFERRED,
+    pos         INT,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW()
 );
 
+CREATE TABLE container.biblio_record_entry_bucket_item_note (
+    id      SERIAL      PRIMARY KEY,
+    item    INT         NOT NULL REFERENCES container.biblio_record_entry_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
 
 
+
 CREATE TABLE container.user_bucket_type (
 	code	TEXT	PRIMARY KEY,
 	label	TEXT	NOT NULL UNIQUE
@@ -153,12 +192,18 @@
 				DEFERRABLE
 				INITIALLY DEFERRED,
 	name	TEXT	NOT NULL,
-	btype	TEXT	NOT NULL DEFAULT 'misc' REFERENCES container.user_bucket_type (code),
+	btype	TEXT	NOT NULL DEFAULT 'misc' REFERENCES container.user_bucket_type (code) DEFERRABLE INITIALLY DEFERRED,
 	pub	BOOL	NOT NULL DEFAULT FALSE,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	CONSTRAINT ub_name_once_per_owner UNIQUE (owner,name,btype)
 );
 
+CREATE TABLE container.user_bucket_note (
+    id      SERIAL      PRIMARY KEY,
+    bucket  INT         NOT NULL REFERENCES container.user_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
+
 CREATE TABLE container.user_bucket_item (
 	id		SERIAL	PRIMARY KEY,
 	bucket		INT	NOT NULL
@@ -173,7 +218,15 @@
 					ON UPDATE CASCADE
 					DEFERRABLE
 					INITIALLY DEFERRED,
+    pos         INT,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW()
 );
 
+CREATE TABLE container.user_bucket_item_note (
+    id      SERIAL      PRIMARY KEY,
+    item    INT         NOT NULL REFERENCES container.user_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    note    TEXT        NOT NULL
+);
+
+
 COMMIT;

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/080.schema.money.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/080.schema.money.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/080.schema.money.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -56,7 +56,7 @@
 	void_time	TIMESTAMP WITH TIME ZONE,
 	amount		NUMERIC(6,2)			NOT NULL,
 	billing_type	TEXT				NOT NULL,
-	btype		INT				NOT NULL REFERENCES config.billing_type (id) ON DELETE RESTRICT,
+	btype		INT				NOT NULL REFERENCES config.billing_type (id) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
 	note		TEXT
 );
 CREATE INDEX m_b_xact_idx ON money.billing (xact);

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/090.schema.action.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/090.schema.action.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/090.schema.action.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -247,6 +247,17 @@
 	FOR EACH ROW
 	EXECUTE PROCEDURE action.circulation_claims_returned ();
 
+CREATE TABLE action.hold_request_cancel_cause (
+    id      SERIAL  PRIMARY KEY,
+    label   TEXT    UNIQUE
+);
+INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (1,'Untargeted expiration');
+INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (2,'Hold Shelf expiration');
+INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (3,'Patron via phone');
+INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (4,'Patron in person');
+INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (5,'Staff forced');
+INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (6,'Patron via OPAC');
+SELECT SETVAL('action.hold_request_cancel_cause_id_seq', 100);
 
 CREATE TABLE action.hold_request (
 	id			SERIAL				PRIMARY KEY,
@@ -258,6 +269,8 @@
 	prev_check_time		TIMESTAMP WITH TIME ZONE,
 	expire_time		TIMESTAMP WITH TIME ZONE,
 	cancel_time		TIMESTAMP WITH TIME ZONE,
+	cancel_cause	INT REFERENCES action.hold_request_cancel_cause (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+	cancel_note		TEXT,
 	target			BIGINT				NOT NULL, -- see hold_type
 	current_copy		BIGINT				REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
 	fulfillment_staff	INT				REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -19,7 +19,7 @@
 CREATE SCHEMA extend_reporter;
 
 CREATE TABLE extend_reporter.legacy_circ_count (
-    id          BIGSERIAL   PRIMARY KEY REFERENCES asset.copy (id),
+    id          BIGSERIAL   PRIMARY KEY REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
     circ_count  INT         NOT NULL DEFAULT 0
 );
 

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -20,7 +20,7 @@
 CREATE SCHEMA extend_reporter;
 
 CREATE TABLE extend_reporter.legacy_circ_count (
-    id          BIGSERIAL   PRIMARY KEY REFERENCES asset.copy (id),
+    id          BIGSERIAL   PRIMARY KEY REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
     circ_count  INT         NOT NULL DEFAULT 0
 );
 

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/200.schema.acq.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/200.schema.acq.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/200.schema.acq.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -20,8 +20,8 @@
 
 CREATE TABLE acq.exchange_rate (
     id              SERIAL  PRIMARY KEY,
-    from_currency   TEXT    NOT NULL REFERENCES acq.currency_type (code),
-    to_currency     TEXT    NOT NULL REFERENCES acq.currency_type (code),
+    from_currency   TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
+    to_currency     TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
     ratio           NUMERIC NOT NULL,
     CONSTRAINT exchange_rate_from_to_once UNIQUE (from_currency,to_currency)
 );
@@ -32,56 +32,94 @@
 CREATE TABLE acq.provider (
 	id		SERIAL	PRIMARY KEY,
 	name		TEXT	NOT NULL,
-	owner		INT	NOT NULL REFERENCES actor.org_unit (id),
-	currency_type	TEXT	NOT NULL REFERENCES acq.currency_type (code),
+	owner		INT	NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
+	currency_type	TEXT	NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
 	code		TEXT	UNIQUE,
 	CONSTRAINT provider_name_once_per_owner UNIQUE (name,owner)
 );
 
+CREATE TABLE acq.provider_address (
+	id		SERIAL	PRIMARY KEY,
+	valid		BOOL	NOT NULL DEFAULT TRUE,
+	address_type	TEXT,
+    provider    INT     NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
+	street1		TEXT	NOT NULL,
+	street2		TEXT,
+	city		TEXT	NOT NULL,
+	county		TEXT,
+	state		TEXT	NOT NULL,
+	country		TEXT	NOT NULL,
+	post_code	TEXT	NOT NULL
+);
+
+CREATE TABLE acq.provider_contact (
+	id		SERIAL	PRIMARY KEY,
+    provider    INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
+    name    TEXT NULL NULL,
+    role    TEXT, -- free-form.. e.g. "our sales guy"
+    email   TEXT,
+    phone   TEXT
+);
+
+CREATE TABLE acq.provider_contact_address (
+	id			SERIAL	PRIMARY KEY,
+	valid			BOOL	NOT NULL DEFAULT TRUE,
+	address_type	TEXT,
+	contact    		INT	    NOT NULL REFERENCES acq.provider_contact (id) DEFERRABLE INITIALLY DEFERRED,
+	street1			TEXT	NOT NULL,
+	street2			TEXT,
+	city			TEXT	NOT NULL,
+	county			TEXT,
+	state			TEXT	NOT NULL,
+	country			TEXT	NOT NULL,
+	post_code		TEXT	NOT NULL
+);
+
+
 CREATE TABLE acq.funding_source (
 	id		SERIAL	PRIMARY KEY,
 	name		TEXT	NOT NULL,
-	owner		INT	NOT NULL REFERENCES actor.org_unit (id),
-	currency_type	TEXT	NOT NULL REFERENCES acq.currency_type (code),
+	owner		INT	NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
+	currency_type	TEXT	NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
 	code		TEXT	UNIQUE,
 	CONSTRAINT funding_source_name_once_per_owner UNIQUE (name,owner)
 );
 
 CREATE TABLE acq.funding_source_credit (
 	id	SERIAL	PRIMARY KEY,
-	funding_source    INT     NOT NULL REFERENCES acq.funding_source (id),
+	funding_source    INT     NOT NULL REFERENCES acq.funding_source (id) DEFERRABLE INITIALLY DEFERRED,
 	amount	NUMERIC	NOT NULL,
 	note	TEXT
 );
 
 CREATE TABLE acq.fund (
     id              SERIAL  PRIMARY KEY,
-    org             INT     NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE,
+    org             INT     NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     name            TEXT    NOT NULL,
     year            INT     NOT NULL DEFAULT EXTRACT( YEAR FROM NOW() ),
-    currency_type   TEXT    NOT NULL REFERENCES acq.currency_type (code),
+    currency_type   TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
     code            TEXT    UNIQUE,
     CONSTRAINT name_once_per_org_year UNIQUE (org,name,year)
 );
 
 CREATE TABLE acq.fund_debit (
 	id			SERIAL	PRIMARY KEY,
-	fund			INT     NOT NULL REFERENCES acq.fund (id),
+	fund			INT     NOT NULL REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
 	origin_amount		NUMERIC	NOT NULL,  -- pre-exchange-rate amount
-	origin_currency_type	TEXT	NOT NULL REFERENCES acq.currency_type (code),
+	origin_currency_type	TEXT	NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
 	amount			NUMERIC	NOT NULL,
 	encumbrance		BOOL	NOT NULL DEFAULT TRUE,
 	debit_type		TEXT	NOT NULL,
-	xfer_destination	INT	REFERENCES acq.fund (id)
+	xfer_destination	INT	REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED
 );
 
 CREATE TABLE acq.fund_allocation (
     id          SERIAL  PRIMARY KEY,
-    funding_source        INT     NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE,
-    fund        INT     NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE,
+    funding_source        INT     NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    fund        INT     NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
     amount      NUMERIC,
     percent     NUMERIC CHECK (percent IS NULL OR percent BETWEEN 0.0 AND 100.0),
-    allocator   INT NOT NULL REFERENCES actor.usr (id),
+    allocator   INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
     note        TEXT,
     CONSTRAINT allocation_amount_or_percent CHECK ((percent IS NULL AND amount IS NOT NULL) OR (percent IS NOT NULL AND amount IS NULL))
 );
@@ -89,8 +127,8 @@
 
 CREATE TABLE acq.picklist (
 	id		SERIAL				PRIMARY KEY,
-	owner		INT				NOT NULL REFERENCES actor.usr (id),
-	org_unit	INT				NOT NULL REFERENCES actor.org_unit (id),
+	owner		INT				NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
+	org_unit	INT				NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
 	name		TEXT				NOT NULL,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	edit_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
@@ -99,11 +137,11 @@
 
 CREATE TABLE acq.purchase_order (
 	id		SERIAL				PRIMARY KEY,
-	owner		INT				NOT NULL REFERENCES actor.usr (id),
-	ordering_agency		INT				NOT NULL REFERENCES actor.org_unit (id),
+	owner		INT				NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
+	ordering_agency		INT				NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	edit_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
-	provider	INT				NOT NULL REFERENCES acq.provider (id),
+	provider	INT				NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
 	state		TEXT				NOT NULL DEFAULT 'new'
 );
 CREATE INDEX po_owner_idx ON acq.purchase_order (owner);
@@ -112,9 +150,9 @@
 
 CREATE TABLE acq.po_note (
 	id		SERIAL				PRIMARY KEY,
-	purchase_order	INT				NOT NULL REFERENCES acq.purchase_order (id),
-	creator		INT				NOT NULL REFERENCES actor.usr (id),
-	editor		INT				NOT NULL REFERENCES actor.usr (id),
+	purchase_order	INT				NOT NULL REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
+	creator		INT				NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
+	editor		INT				NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	edit_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	value		TEXT				NOT NULL
@@ -123,15 +161,15 @@
 
 CREATE TABLE acq.lineitem (
 	id                  BIGSERIAL                   PRIMARY KEY,
-	selector            INT                         NOT NULL REFERENCES actor.org_unit (id),
-	provider            INT                         REFERENCES acq.provider (id),
-	purchase_order      INT                         REFERENCES acq.purchase_order (id),
-	picklist            INT                         REFERENCES acq.picklist (id),
+	selector            INT                         NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
+	provider            INT                         REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
+	purchase_order      INT                         REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
+	picklist            INT                         REFERENCES acq.picklist (id) DEFERRABLE INITIALLY DEFERRED,
 	expected_recv_time  TIMESTAMP WITH TIME ZONE,
 	create_time         TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
 	edit_time           TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
 	marc                TEXT                        NOT NULL,
-	eg_bib_id           INT                         REFERENCES biblio.record_entry (id),
+	eg_bib_id           INT                         REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
 	source_label        TEXT,
 	item_count          INT                         NOT NULL DEFAULT 0,
 	state               TEXT                        NOT NULL DEFAULT 'new',
@@ -142,9 +180,9 @@
 
 CREATE TABLE acq.lineitem_note (
 	id		SERIAL				PRIMARY KEY,
-	lineitem	INT				NOT NULL REFERENCES acq.lineitem (id),
-	creator		INT				NOT NULL REFERENCES actor.usr (id),
-	editor		INT				NOT NULL REFERENCES actor.usr (id),
+	lineitem	INT				NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
+	creator		INT				NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
+	editor		INT				NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
 	create_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	edit_time	TIMESTAMP WITH TIME ZONE	NOT NULL DEFAULT NOW(),
 	value		TEXT				NOT NULL
@@ -153,14 +191,14 @@
 
 CREATE TABLE acq.lineitem_detail (
 	id		BIGSERIAL			PRIMARY KEY,
-	lineitem	INT				NOT NULL REFERENCES acq.lineitem (id),
-	fund		INT				REFERENCES acq.fund (id),
-	fund_debit	INT				REFERENCES acq.fund_debit (id),
-	eg_copy_id	BIGINT			REFERENCES asset.copy (id) ON DELETE SET NULL,
+	lineitem	INT				NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
+	fund		INT				REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
+	fund_debit	INT				REFERENCES acq.fund_debit (id) DEFERRABLE INITIALLY DEFERRED,
+	eg_copy_id	BIGINT			REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
 	barcode		TEXT,
 	cn_label	TEXT,
-    owning_lib  INT             REFERENCES actor.org_unit (id) ON DELETE SET NULL,
-    location    INT             REFERENCES asset.copy_location (id) ON DELETE SET NULL,
+    owning_lib  INT             REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
+    location    INT             REFERENCES asset.copy_location (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
 	recv_time	TIMESTAMP WITH TIME ZONE
 );
 
@@ -182,7 +220,7 @@
 CREATE TABLE acq.lineitem_provider_attr_definition (
 	id		BIGINT	PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
 	xpath		TEXT		NOT NULL,
-	provider	INT	NOT NULL REFERENCES acq.provider (id)
+	provider	INT	NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED
 ) INHERITS (acq.lineitem_attr_definition);
 
 CREATE TABLE acq.lineitem_generated_attr_definition (
@@ -192,7 +230,7 @@
 
 CREATE TABLE acq.lineitem_usr_attr_definition (
 	id		BIGINT	PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
-	usr		INT	NOT NULL REFERENCES actor.usr (id)
+	usr		INT	NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED
 ) INHERITS (acq.lineitem_attr_definition);
 
 CREATE TABLE acq.lineitem_local_attr_definition (
@@ -202,7 +240,7 @@
 CREATE TABLE acq.lineitem_attr (
 	id		BIGSERIAL	PRIMARY KEY,
 	definition	BIGINT		NOT NULL,
-	lineitem	BIGINT		NOT NULL REFERENCES acq.lineitem (id),
+	lineitem	BIGINT		NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
 	attr_type	TEXT		NOT NULL,
 	attr_name	TEXT		NOT NULL,
 	attr_value	TEXT		NOT NULL

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/210.schema.serials.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/210.schema.serials.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/210.schema.serials.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -10,7 +10,7 @@
 	active	BOOL	NOT NULL DEFAULT TRUE
 );
 
-ALTER TABLE asset.call_number ADD COLUMN uri INT REFERENCES asset.uri (id);
+ALTER TABLE asset.call_number ADD COLUMN uri INT REFERENCES asset.uri (id) DEFERRABLE INITIALLY DEFERRED;
 
 BEGIN;
 

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -87,12 +87,12 @@
 CREATE TABLE action_trigger.event_definition (
     id              SERIAL      PRIMARY KEY,
     active          BOOL        NOT NULL DEFAULT TRUE,
-    owner           INT         NOT NULL REFERENCES actor.org_unit (id),
-    hook            TEXT        NOT NULL REFERENCES action_trigger.hook (key),
-    validator       TEXT        NOT NULL REFERENCES action_trigger.validator (module),
-    reactor         TEXT        NOT NULL REFERENCES action_trigger.reactor (module),
-    cleanup_success TEXT        REFERENCES action_trigger.cleanup (module),
-    cleanup_failure TEXT        REFERENCES action_trigger.cleanup (module),
+    owner           INT         NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
+    hook            TEXT        NOT NULL REFERENCES action_trigger.hook (key) DEFERRABLE INITIALLY DEFERRED,
+    validator       TEXT        NOT NULL REFERENCES action_trigger.validator (module) DEFERRABLE INITIALLY DEFERRED,
+    reactor         TEXT        NOT NULL REFERENCES action_trigger.reactor (module) DEFERRABLE INITIALLY DEFERRED,
+    cleanup_success TEXT        REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED,
+    cleanup_failure TEXT        REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED,
     delay           INTERVAL    NOT NULL DEFAULT '5 minutes',
     delay_field     TEXT,                 -- for instance, xact_start on a circ hook ... look for fields on hook.core_type where datatype=timestamp? If not set, delay from now()
     group_field     TEXT,                 -- field from this.hook.core_type to batch event targets together on, fed into reactor a group at a time.
@@ -102,11 +102,11 @@
 
 CREATE TABLE action_trigger.environment (
     id          SERIAL  PRIMARY KEY,
-    event_def   INT     NOT NULL REFERENCES action_trigger.event_definition (id),
+    event_def   INT     NOT NULL REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
     path        TEXT,       -- fields to flesh. given a hook with a core_type of circ, imagine circ_lib.parent_ou expanding to
                             -- {flesh: 2, flesh_fields: {circ: ['circ_lib'], aou: ['parent_ou']}} ... default is to flesh all
                             -- at flesh depth 1
-    collector   TEXT    REFERENCES action_trigger.collector (module), -- if set, given the object at 'path', return some data
+    collector   TEXT    REFERENCES action_trigger.collector (module) DEFERRABLE INITIALLY DEFERRED, -- if set, given the object at 'path', return some data
                                                                       -- to be stashed at environment.<label>
     label       TEXT    CHECK (label NOT IN ('result','target','event')),
     CONSTRAINT env_event_label_once UNIQUE (event_def,label)
@@ -115,7 +115,7 @@
 CREATE TABLE action_trigger.event (
     id              BIGSERIAL   PRIMARY KEY,
     target          BIGINT      NOT NULL, -- points at the id from class defined by event_def.hook.core_type
-    event_def       INT         REFERENCES action_trigger.event_definition (id),
+    event_def       INT         REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
     add_time        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
     run_time        TIMESTAMPTZ NOT NULL,
     start_time      TIMESTAMPTZ,
@@ -129,11 +129,11 @@
 
 CREATE TABLE action_trigger.event_params (
     id          BIGSERIAL   PRIMARY KEY,
-    event_def   INT         NOT NULL REFERENCES action_trigger.event_definition (id),
+    event_def   INT         NOT NULL REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
     param       TEXT        NOT NULL, -- the key under environment.event.params to store the output of ...
-    value       TEXT        NOT NULL, -- ... the eval() output of this.  Has access to environmen (and, well, all of perl)
+    value       TEXT        NOT NULL, -- ... the eval() output of this.  Has access to environment (and, well, all of perl)
     CONSTRAINT event_params_event_def_param_once UNIQUE (event_def,param)
 );
 
---COMMIT;
+COMMIT;
 

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/500.view.cross-schema.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/500.view.cross-schema.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/500.view.cross-schema.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -81,5 +81,16 @@
 	  WHERE	xact_type = 'circulation'
 	  GROUP BY 1;
 
+-- Not a view, but it's cross-schema..
+CREATE TABLE config.idl_field_doc (
+    id              BIGSERIAL   PRIMARY KEY,
+    fm_class        TEXT        NOT NULL,
+    field           TEXT        NOT NULL,
+    owner           INT         NOT NULL    REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    string          TEXT        NOT NULL
+);
+CREATE UNIQUE INDEX idl_field_doc_identity ON config.idl_field_doc (fm_class,field,owner);
+
+
 COMMIT;
 

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/950.data.seed-values.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/950.data.seed-values.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/950.data.seed-values.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -1139,6 +1139,8 @@
 INSERT INTO permission.perm_list VALUES 
     (151, 'DELETE_CONTAINER_ITEM', oils_i18n_gettext(151, 'Allow a user to delete an item out of another user''s container', 'ppl', 'description'));
 INSERT INTO permission.perm_list VALUES 
+    (152, 'ASSIGN_WORK_ORG_UNIT', oils_i18n_gettext(152, 'Allow a staff member to define where another staff member has their permissions', 'ppl', 'description'));
+INSERT INTO permission.perm_list VALUES 
     (153, 'CREATE_FUNDING_SOURCE', oils_i18n_gettext(153, 'Allow a user to create a new funding source', 'ppl', 'description')),
     (154, 'DELETE_FUNDING_SOURCE', oils_i18n_gettext(154, 'Allow a user to delete a funding source', 'ppl', 'description')),
     (155, 'VIEW_FUNDING_SOURCE', oils_i18n_gettext(155, 'Allow a user to view a funding source', 'ppl', 'description')),
@@ -1164,13 +1166,10 @@
     (175, 'ADMIN_PROVIDER', oils_i18n_gettext(175, 'Allow a user to create/view/update/delete a provider', 'ppl', 'description')),
     (176, 'MANAGE_PROVIDER', oils_i18n_gettext(176, 'Allow a user to view and purchase from a provider', 'ppl', 'description')),
     (177, 'VIEW_PICKLIST', oils_i18n_gettext(177, 'Allow a user to view another users picklist', 'ppl', 'description')),
-    (152, 'ASSIGN_WORK_ORG_UNIT', oils_i18n_gettext(152, 'Allow a staff member to define where another staff member has their permissions', 'ppl', 'description'));
-INSERT INTO permission.perm_list VALUES 
-    (178, 'DELETE_RECORD', oils_i18n_gettext(178, 'Allow a staff member to directly remove a bibliographic record', 'ppl', 'description'));
-INSERT INTO permission.perm_list VALUES 
-    (179, 'ADMIN_CURRENCY_TYPE', oils_i18n_gettext(179, 'Allow a user to create/view/update/delete a currency_type', 'ppl', 'description'));
-INSERT INTO permission.perm_list VALUES 
-    (180, 'MARK_BAD_DEBT', oils_i18n_gettext(180, 'Allow a user to mark a transaction as bad (unrecoverable) debt', 'ppl', 'description'));
+    (178, 'DELETE_RECORD', oils_i18n_gettext(178, 'Allow a staff member to directly remove a bibliographic record', 'ppl', 'description')),
+    (179, 'ADMIN_CURRENCY_TYPE', oils_i18n_gettext(179, 'Allow a user to create/view/update/delete a currency_type', 'ppl', 'description')),
+    (180, 'MARK_BAD_DEBT', oils_i18n_gettext(180, 'Allow a user to mark a transaction as bad (unrecoverable) debt', 'ppl', 'description')),
+    (181, 'VIEW_BILLING_TYPE', oils_i18n_gettext(181, 'Allow a user to view billing types', 'ppl', 'description'));
 
 SELECT SETVAL('permission.perm_list_id_seq'::TEXT, (SELECT MAX(id) FROM permission.perm_list));
 
@@ -1396,6 +1395,7 @@
 INSERT INTO permission.grp_perm_map VALUES (113, 3, 97, 0, false);
 INSERT INTO permission.grp_perm_map VALUES (130, 3, 99, 1, false);
 INSERT INTO permission.grp_perm_map VALUES (131, 3, 100, 1, false);
+INSERT INTO permission.grp_perm_map VALUES (139, 3, 181, 0, false);
 INSERT INTO permission.grp_perm_map VALUES (22, 4, 18, 0, false);
 INSERT INTO permission.grp_perm_map VALUES (24, 4, 20, 0, false);
 INSERT INTO permission.grp_perm_map VALUES (38, 4, 21, 2, false);
@@ -1422,6 +1422,9 @@
 INSERT INTO permission.grp_perm_map VALUES (132, 10, 101, 1, true);
 INSERT INTO permission.grp_perm_map VALUES (136, 10, 102, 1, false);
 INSERT INTO permission.grp_perm_map VALUES (137, 10, 103, 1, false);
+INSERT INTO permission.grp_perm_map VALUES (140, 10, 147, 1, false);
+INSERT INTO permission.grp_perm_map VALUES (141, 10, 148, 1, false);
+INSERT INTO permission.grp_perm_map VALUES (142, 10, 149, 1, false);
 INSERT INTO permission.grp_perm_map VALUES (97, 5, 41, 0, false);
 INSERT INTO permission.grp_perm_map VALUES (96, 5, 43, 0, false);
 INSERT INTO permission.grp_perm_map VALUES (93, 5, 48, 0, false);
@@ -1441,6 +1444,9 @@
 -- Admin user permissions
 INSERT INTO permission.usr_perm_map (usr,perm,depth) VALUES (1,-1,0);
 
+-- Set a work_ou for the Administrator user
+INSERT INTO permission.usr_work_ou_map (usr, work_ou) VALUES (1, 1);
+
 --010.schema.biblio.sql:
 INSERT INTO biblio.record_entry VALUES (-1,1,1,1,-1,NOW(),NOW(),FALSE,FALSE,'','AUTOGEN','-1','','FOO');
 
@@ -1573,6 +1579,7 @@
 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('misc', oils_i18n_gettext('misc', 'Miscellaneous', 'cbrebt', 'label'));
 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.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'));

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/999.functions.global.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/999.functions.global.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/999.functions.global.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -189,8 +189,10 @@
 BEGIN
     SELECT INTO old_id replaces FROM actor.usr_address where id = pending_id;
     IF old_id IS NULL THEN
-        RETURN NULL;
+        UPDATE actor.usr_address SET pending = 'f' WHERE id = pending_id;
+        RETURN pending_id;
     END IF;
+    -- address replaces an existing address
     DELETE FROM actor.usr_address WHERE id = -old_id;
     UPDATE actor.usr_address SET id = -id WHERE id = old_id;
     UPDATE actor.usr_address SET replaces = NULL, id = old_id WHERE id = pending_id;

Modified: branches/staff-client-experiment/Open-ILS/src/sql/Pg/extend-reporter.sql
===================================================================
--- branches/staff-client-experiment/Open-ILS/src/sql/Pg/extend-reporter.sql	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/src/sql/Pg/extend-reporter.sql	2009-02-19 05:39:53 UTC (rev 12228)
@@ -21,7 +21,7 @@
 CREATE SCHEMA extend_reporter;
 
 CREATE TABLE extend_reporter.legacy_circ_count (
-    id          BIGINT   PRIMARY KEY REFERENCES asset.copy (id),
+    id          BIGINT   PRIMARY KEY REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
     circ_count  INT         NOT NULL DEFAULT 0
 );
 

Modified: branches/staff-client-experiment/Open-ILS/tests/datasets/README
===================================================================
--- branches/staff-client-experiment/Open-ILS/tests/datasets/README	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/tests/datasets/README	2009-02-19 05:39:53 UTC (rev 12228)
@@ -9,3 +9,4 @@
 | map_data.marc     | MARC21    | UTF8     | Voyager (LoC) | 3 records with some geospatial metadata  |
 | music_5k.marc     | MARC21    | MARC8    | Unicorn GL3.1 | 5000 records  |
 | hebrew.marc       | MARC21    | MARC8    | III           | Hebrew scripts, 25 records |
+| nepali.marc       | MARC21    | UTF8     |               | Nepali scripts, 98 records, dual 245 and 100 fields (one for Nepali, one for phoneticization |

Copied: branches/staff-client-experiment/Open-ILS/tests/datasets/nepali.marc (from rev 12227, trunk/Open-ILS/tests/datasets/nepali.marc)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/conify/global/admin.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/conify/global/admin.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/conify/global/admin.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -3,9 +3,9 @@
 if (location.href.match(/^.*conify\/(.+)\/global.*$/, "$1")) {
 	var _url_locale = location.href.replace(/^.*conify\/(.+)\/global.*$/, "$1").replace(/_/,'-','g');
 
-	if (_url_locale) djConfig.locale = _url_locale;
+	if (_url_locale) djConfig.locale = _url_locale.toLowerCase();
 
 } else {
 	var _url_locale = '<!--#echo var="locale"-->';
-	if (_url_locale != '(none)') djConfig.locale = _url_locale;
+	if (_url_locale != '(none)') djConfig.locale = _url_locale.toLowerCase();
 }

Modified: branches/staff-client-experiment/Open-ILS/web/css/skin/default.css
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/css/skin/default.css	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/css/skin/default.css	2009-02-19 05:39:53 UTC (rev 12228)
@@ -5,11 +5,9 @@
 @import "/js/dojo/dojo/resources/dojo.css";
 @import "/js/dojo/dijit/themes/tundra/tundra.css";
 @import "/js/dojo/dojox/grid/_grid/Grid.css";
- at import "/js/dojo/dojox/grid/resources/Grid.css";
 @import "/js/dojo/dojox/grid/resources/tundraGrid.css";
 
 
-
 html, body, #oils-base-body-block {
     width:100%;
     height:100%;
@@ -18,6 +16,7 @@
     padding:0;
 }
 table { border-collapse: collapse; }
+
 /* use this for divs whose contents should be entirely contained within the div */
 .container:after {content: ""; display: block; height: 0; clear: both; }
 
@@ -65,4 +64,20 @@
 
 
 .oils-fm-edit-dialog { margin: 5px; }
-.oils-fm-edit-dialog td { padding: 5px; }
+.oils-fm-edit-dialog td { padding: 5px; border:1px solid #999;}
+.oils-header-panel {
+    width:100%;
+    margin-top:20px;
+}
+.oils-header-panel div:first-child {
+    width:48%;
+    text-align:left;
+    float:left;
+    font-size:130%;
+    font-weight: bold;
+}
+.oils-header-panel div:last-child {
+    width:48%;
+    text-align:right;
+    float:right;
+}

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -295,7 +295,8 @@
 		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_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 : ['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']
 	};
 
 }

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/IDL.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/IDL.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/IDL.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -41,15 +41,20 @@
                 var id = node.getAttribute('id');
                 var fields = node.getElementsByTagName('fields')[0];
                 window.fmclasses[id] = [];
+                
+                var fieldData = this._parseFields(node, id);
     
                 var obj = { 
-                    fields  : this._parseFields(node, id),
+                    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    : fields.getAttributeNS(this.NS_PERSIST, 'primary'),
+                    pkey_sequence : fields.getAttributeNS(this.NS_PERSIST, 'sequence')
                 };
 
                 var permacrud = node.getElementsByTagName('permacrud')[0];
@@ -90,6 +95,7 @@
         /* parses the links and fields portion of the IDL */
         _parseFields : function(node, classname) {
             var data = [];
+            var map = {};
     
             var fields = node.getElementsByTagName('fields')[0];
             fields = fields.getElementsByTagName('field');
@@ -137,6 +143,7 @@
                 } 
     
                 data.push(obj);
+                map[obj.name] = obj;
             }
     
             /*
@@ -149,7 +156,7 @@
             );
             */
     
-            return data;
+            return { list : data, map : map };
         }
 
     });

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/OrgUtils.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/OrgUtils.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/fieldmapper/OrgUtils.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -100,9 +100,21 @@
    /* ---------------------------------------------------------------------- */
 
 	fieldmapper.aou.prototype.fetchOrgSettingDefault = function (name) {
-		return this.standardRequest( fieldmapper.OpenSRF.methods.FETCH_ORG_SETTING, name ); 
+		return this.standardRequest( fieldmapper.OpenSRF.methods.FETCH_ORG_SETTING, [this.id(), name] ); 
 	}
 
+	fieldmapper.aou.prototype.fetchOrgSettingBatch = function (nameList) {
+		return this.standardRequest( fieldmapper.OpenSRF.methods.FETCH_ORG_SETTING_BATCH, [this.id(), nameList] ); 
+	}
+
+	fieldmapper.aou.fetchOrgSettingDefault = function (orgId, name) {
+		return fieldmapper.standardRequest( fieldmapper.OpenSRF.methods.FETCH_ORG_SETTING, [orgId, name] ); 
+	}
+
+	fieldmapper.aou.fetchOrgSettingBatch = function (orgId, nameList) {
+		return fieldmapper.standardRequest( fieldmapper.OpenSRF.methods.FETCH_ORG_SETTING_BATCH, [orgId, nameList] ); 
+	}
+
 	fieldmapper.aout.findOrgType = function (id) {
 		fieldmapper.aout.LoadOrgTypes();
 		return fieldmapper.aout.OrgTypeCache[id].type;

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/User.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/User.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/User.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -23,6 +23,7 @@
     dojo.require('openils.Event');
     dojo.require('fieldmapper.Fieldmapper');
     dojo.require('fieldmapper.OrgUtils');
+    dojo.require('openils.Util');
 
     dojo.declare('openils.User', null, {
 

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/Util.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/Util.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/Util.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -204,4 +204,18 @@
         }
         return map;
     };
+
+    /**
+     * Assume a space-separated interval string, with optional comma
+     * E.g. "1 year, 2 days"  "3 days 6 hours"
+     */
+    openils.Util.intervalToSeconds = function(interval) {
+        var d = new Date();
+        var start = d.getTime();
+        var parts = interval.split(' ');
+        for(var i = 0; i < parts.length; i += 2) 
+            d = dojo.date.add(d, parts[i+1].replace(/s?,?$/,''), Number(parts[i]));
+        return Number((d.getTime() - start) / 1000);
+    };
 }
+

Copied: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/XUL.js (from rev 12227, trunk/Open-ILS/web/js/dojo/openils/XUL.js)
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/XUL.js	                        (rev 0)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/XUL.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -0,0 +1,25 @@
+if(!dojo._hasResource["openils.XUL"]) {
+
+    dojo.provide("openils.XUL");
+    dojo.declare('openils.XUL', null, {});
+
+    openils.XUL.isXUL = function() {
+        return window.IAMXUL;
+    }
+    
+    openils.XUL.getStash = function() {
+        if(openils.XUL.isXUL()) {
+            try {
+			    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+			    var CacheClass = new Components.Constructor("@mozilla.org/openils_data_cache;1", "nsIOpenILS");
+			    return new CacheClass().wrappedJSObject.OpenILS.prototype.data;
+            } catch(e) {
+                console.log("Error loading XUL stash: " + e);
+            }
+        }
+
+        return {};
+    };
+}
+
+

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/acq/CurrencyType.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/acq/CurrencyType.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/acq/CurrencyType.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -19,6 +19,8 @@
     dojo._hasResource["openils.acq.CurrencyType"] = true;
     dojo.provide("openils.acq.CurrencyType");
     dojo.require('openils.User');
+    dojo.require('openils.Util');
+    dojo.require('openils.PermaCrud');
 
     dojo.declare('openils.acq.CurrencyType', null, {
     });
@@ -29,19 +31,19 @@
      * Retrieves all of the currency types
      */
     openils.acq.CurrencyType.fetchAll = function(onComplete) {
-        var req = new OpenSRF.ClientSession('open-ils.acq').request(
-            'open-ils.acq.currency_type.all.retrieve', openils.User.authtoken);
+        var list = [];
+        var pcrud = new openils.PermaCrud();
+        pcrud.retrieveAll('acqct', {
+            async : true,
+            oncomplete : function(r) {
+                var types = openils.Util.readResponse(r);
+                for(var idx in types)
+                    openils.acq.CurrencyType.cache[types[idx].code()] = types[idx];
+                onComplete(types);
+            }
+        });
+    };
 
-        req.oncomplete = function(r) {
-            var msg = r.recv();
-            var types = msg.content();
-            for(var i in types) 
-                openils.acq.CurrencyType.cache[types[i].code()] = types[i];
-            onComplete(types);
-        }
-        req.send();
-    }
-
     openils.acq.CurrencyType.loadSelectWidget = function(selector) {
         openils.acq.CurrencyType.fetchAll(
             function(ctypes) {
@@ -50,6 +52,6 @@
                 selector.setValue(ctypes[0].code()); /* XXX get from setting */
             }
         );
-    }
+    };
 }
 

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/editors.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/editors.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/editors.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -44,7 +44,6 @@
 dojo.declare('openils.editors.ProviderSelectEditor', dojox.grid.editors.Dijit, {
     editorClass: "openils.widget.ProviderSelector",
     createEditor: function(inNode, inDatum, inRowIndex) {
-	console.log("openils.widget.ProviderSelectEditor");
 	var editor = new this.editorClass(this.getEditorProps(inDatum), inNode);
 	openils.acq.Provider.buildPermProviderSelector(this.cell.perm || this.perm,
 						       editor);

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -7,6 +7,7 @@
     dojo.declare('openils.widget.AutoFieldWidget', null, {
 
         async : false,
+        cache : {},
 
         /**
          * args:
@@ -26,10 +27,13 @@
                 this[k] = args[k];
 
             // find the field description in the IDL if not provided
+            if(this.fmObject) 
+                this.fmClass = this.fmObject.classname;
+            this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
+
             if(!this.idlField) {
-                if(this.fmObject)
-                    this.fmClass = this.fmObject.classname;
-                var fields = fieldmapper.IDL.fmclasses[this.fmClass].fields;
+                this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
+                var fields = this.fmIDL.fields;
                 for(var f in fields) 
                     if(fields[f].name == this.fmField)
                         this.idlField = fields[f];
@@ -37,10 +41,14 @@
         },
 
         /**
-         * Turn the value from the dojo widget into a value oils understands
+         * Turn the widget-stored value into a value oils understands
          */
         getFormattedValue : function() {
-            var value = this.widget.attr('value');
+            var value = this.baseWidgetValue();
+
+            /* text widgets default to "" when no data is entered */
+            if(value == '') return null; 
+
             switch(this.idlField.datatype) {
                 case 'bool':
                     return (value) ? 't' : 'f'
@@ -50,7 +58,16 @@
                     return value;
             }
         },
+
+        baseWidgetValue : function(value) {
+            var attr = (this.readOnly) ? 'content' : 'value';
+            if(arguments.length) this.widget.attr(attr, value);
+            return this.widget.attr(attr);
+        },
         
+        /**
+         * Turn the widget-stored value into something visually suitable
+         */
         getDisplayString : function() {
             var value = this.widgetValue;
             switch(this.idlField.datatype) {
@@ -64,61 +81,147 @@
                 case 'org_unit':
                     return fieldmapper.aou.findOrgUnit(value).shortname();
                 default:
-                    return value;
+                    return value+'';
             }
         },
 
         build : function(onload) {
+
+            if(this.widget) {
+                // core widget provided for us, attach and move on
+                if(this.parentNode) // may already be in the "right" place
+                    this.parentNode.appendChild(this.widget.domNode);
+                return;
+            }
+
             this.onload = onload;
             if(this.widgetValue == null)
                 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
 
-            switch(this.idlField.datatype) {
-                
-                case 'id':
-                    dojo.require('dijit.form.TextBox');
-                    this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
-                    this.widget.attr('disabled', true); // never allow editing of IDs
-                    break;
+            if(this.readOnly) {
+                dojo.require('dijit.layout.ContentPane');
+                this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
 
-                case 'org_unit':
-                    this._buildOrgSelector();
-                    break;
+            } else if(this.widgetClass) {
+                dojo.require(this.widgetClass);
+                eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
 
-                case 'money':
-                    dojo.require('dijit.form.CurrencyTextBox');
-                    this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
-                    break;
+            } else {
 
-                case 'timestamp':
-                    dojo.require('dijit.form.DateTextBox');
-                    dojo.require('dojo.date.stamp');
-                    this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
-                    if(this.widgetValue != null) 
-                        this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
-                    break;
+                switch(this.idlField.datatype) {
+                    
+                    case 'id':
+                        dojo.require('dijit.form.TextBox');
+                        this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
+                        this.widget.attr('disabled', true); // never allow editing of IDs
+                        break;
 
-                case 'bool':
-                    dojo.require('dijit.form.CheckBox');
-                    this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
-                    this.widgetValue = openils.Util.isTrue(this.widgetValue);
-                    break;
+                    case 'org_unit':
+                        this._buildOrgSelector();
+                        break;
 
-                default:
-                    dojo.require('dijit.form.TextBox');
-                    this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
+                    case 'money':
+                        dojo.require('dijit.form.CurrencyTextBox');
+                        this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
+                        break;
+
+                    case 'int':
+                        dojo.require('dijit.form.NumberTextBox');
+                        this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
+                        this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
+                        break;
+
+                    case 'float':
+                        dojo.require('dijit.form.NumberTextBox');
+                        this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
+                        break;
+
+                    case 'timestamp':
+                        dojo.require('dijit.form.DateTextBox');
+                        dojo.require('dojo.date.stamp');
+                        this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
+                        if(this.widgetValue != null) 
+                            this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
+                        break;
+
+                    case 'bool':
+                        dojo.require('dijit.form.CheckBox');
+                        this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
+                        this.widgetValue = openils.Util.isTrue(this.widgetValue);
+                        break;
+
+                    case 'link':
+                        if(this._buildLinkSelector()) break;
+
+                    default:
+                        dojo.require('dijit.form.TextBox');
+                        this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
+                }
             }
 
             if(!this.async) this._widgetLoaded();
             return this.widget;
         },
 
+        _buildLinkSelector : function() {
+
+            /* verify we can and should grab the related class */
+            var linkClass = this.idlField['class'];
+            if(this.idlField.reltype != 'has_a')  return false;
+            if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
+            if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
+
+            dojo.require('openils.PermaCrud');
+            dojo.require('dojo.data.ItemFileReadStore');
+            dojo.require('dijit.form.FilteringSelect');
+
+            var self = this;
+            var vfield;
+            var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
+
+            if(linkClass == 'pgt')
+                return self._buildPermGrpSelector();
+
+            this.async = true;
+            this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
+
+            for(var f in rclassIdl.fields) {
+                if(self.idlField.key == rclassIdl.fields[f].name) {
+                    vfield = rclassIdl.fields[f];
+                    break;
+                }
+            }
+
+            this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
+            this.widget.valueAttr = vfield.name;
+
+            new openils.PermaCrud().retrieveAll(linkClass, {   
+                async : true,
+                oncomplete : function(r) {
+                    var list = openils.Util.readResponse(r, false, true);
+                    if(list) {
+                        self.widget.store = 
+                            new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
+                    }
+                    self.widget.startup();
+                    self._widgetLoaded();
+                }
+            });
+
+            return true;
+        },
+
         /**
          * For widgets that run asynchronously, provide a callback for finishing up
          */
         _widgetLoaded : function(value) {
-            if(this.widgetValue != null) 
-                this.widget.attr('value', this.widgetValue);
+            if(this.readOnly) {
+                this.baseWidgetValue(this.getDisplayString());
+            } else {
+                this.baseWidgetValue(this.widgetValue);
+                if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
+                    this.widget.attr('disabled', true); 
+            }
             if(this.onload)
                 this.onload(this.widget, self);
         },
@@ -148,6 +251,44 @@
                 this.widget.tree = fieldmapper.aou.globalOrgTree;
                 this.widget.startup();
             }
+        },
+
+        _buildPermGrpSelector : function() {
+            dojo.require('openils.widget.FilteringTreeSelect');
+            this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
+            this.widget.searchAttr = 'name';
+
+            if(this.cache.permGrpTree) {
+                this.widget.tree = this.cache.permGrpTree;
+                this.widget.startup();
+                return;
+            } 
+
+            var self = this;
+            this.async = true;
+            new openils.PermaCrud().retrieveAll('pgt', {
+                async : true,
+                oncomplete : function(r) {
+                    var list = openils.Util.readResponse(r, false, true);
+                    if(!list) return;
+                    var map = {};
+                    var root = null;
+                    for(var l in list)
+                        map[list[l].id()] = list[l];
+                    for(var l in list) {
+                        var node = list[l];
+                        var pnode = map[node.parent()];
+                        if(!pnode) {root = node; continue;}
+                        if(!pnode.children()) pnode.children([]);
+                        pnode.children().push(node);
+                    }
+                    self.widget.tree = self.cache.permGrpTree = root;
+                    self.widget.startup();
+                    self._widgetLoaded();
+                }
+            });
+
+            return true;
         }
     });
 }

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/AutoGrid.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -3,32 +3,195 @@
     dojo.require('dojox.grid.DataGrid');
     dojo.require('openils.widget.AutoWidget');
     dojo.require('openils.widget.AutoFieldWidget');
+    dojo.require('openils.widget.EditDialog');
     dojo.require('openils.Util');
 
     dojo.declare(
         'openils.widget.AutoGrid',
         [dojox.grid.DataGrid, openils.widget.AutoWidget],
         {
+
+            /* if true, pop up an edit dialog when user hits Enter on a give row */
+            editOnEnter : false, 
+            defaultCellWidth : null,
+
+            /* by default, don't show auto-generated (sequence) fields */
+            showSequenceFields : false, 
+
             startup : function() {
+                this.selectionMode = 'single';
                 this.inherited(arguments);
                 this.initAutoEnv();
-                var existing = (this.structure) ? this.structure[0].cells[0] : [];
+                this.setStructure(this._compileStructure());
+                this.setStore(this.buildAutoStore());
+                this.overrideEditWidgets = {};
+                if(this.editOnEnter) 
+                    this._applyEditOnEnter();
+                else if(this.singleEditStyle) 
+                    this._applySingleEditStyle();
+            },
+
+            _compileStructure : function() {
+                var existing = (this.structure && this.structure[0].cells[0]) ? 
+                    this.structure[0].cells[0] : [];
                 var fields = [];
+
+                var self = this;
+                function pushEntry(entry) {
+                    if(!entry.get) 
+                        entry.get = openils.widget.AutoGrid.defaultGetter
+                    if(!entry.width && self.defaultCellWidth)
+                        entry.width = self.defaultCellWidth;
+                    fields.push(entry);
+                }
+
+                if(!this.fieldOrder) {
+                    /* no order defined, start with any explicit grid fields */
+                    for(var e in existing) {
+                        var entry = existing[e];
+                        var field = this.fmIDL.fields.filter(
+                            function(i){return (i.name == entry.field)})[0];
+                        if(field) entry.name = entry.name || field.label;
+                        pushEntry(entry);
+                    }
+                }
+
                 for(var f in this.sortedFieldList) {
                     var field = this.sortedFieldList[f];
                     if(!field || field.virtual) continue;
+                    
+                    // field was already added above
+                    if(fields.filter(function(i){return (i.field == field.name)})[0]) 
+                        continue;
+
+
+                    if(!this.showSequenceFields && field.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
+                        continue; 
                     var entry = existing.filter(function(i){return (i.field == field.name)})[0];
                     if(entry) entry.name = field.label;
                     else entry = {field:field.name, name:field.label};
-                    fields.push(entry);
-                    if(!entry.get) 
-                        entry.get = openils.widget.AutoGrid.defaultGetter
+                    pushEntry(entry);
                 }
-                this.setStructure([{cells: [fields]}]);
+
+                return [{cells: [fields]}];
+            },
+
+            _applySingleEditStyle : function() {
+                this.onMouseOverRow = function(e) {};
+                this.onMouseOutRow = function(e) {};
+                this.onCellFocus = function(cell, rowIndex) { 
+                    this.selection.deselectAll();
+                    this.selection.select(this.focus.rowIndex);
+                };
+            },
+
+            /* capture keydown and launch edit dialog on enter */
+            _applyEditOnEnter : function() {
+                this._applySingleEditStyle();
+
+                dojo.connect(this, 'onRowDblClick',
+                    function(e) {
+                        this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
+                    }
+                );
+
+                dojo.connect(this, 'onKeyDown',
+                    function(e) {
+                        if(e.keyCode == dojo.keys.ENTER) {
+                            this.selection.deselectAll();
+                            this.selection.select(this.focus.rowIndex);
+                            this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
+                        }
+                    }
+                );
+            },
+
+            _drawEditDialog : function(storeItem, rowIndex) {
+                var grid = this;
+                var fmObject = new fieldmapper[this.fmClass]().fromStoreItem(storeItem);
+                var idents = grid.store.getIdentityAttributes();
+                var dialog = new openils.widget.EditDialog({
+                    fmObject:fmObject,
+                    overrideWidgets : this.overrideEditWidgets,
+                    onPostSubmit : function() {
+                        for(var i in fmObject._fields) {
+                            var field = fmObject._fields[i];
+                            if(idents.filter(function(j){return (j == field)})[0])
+                                continue; // don't try to edit an identifier field
+                            grid.store.setValue(storeItem, field, fmObject[field]());
+                        }
+                        dialog.destroy();
+
+                        if(self.onPostUpdate)
+                            self.onPostUpdate(storeItem, rowIndex);
+
+                        setTimeout(
+                            function(){
+                                try { 
+                                    grid.views.views[0].getCellNode(rowIndex, 0).focus(); 
+                                } catch (E) {}
+                            },200
+                        );
+                    },
+                    onCancel : function() {
+                        setTimeout(function(){
+                            grid.views.views[0].getCellNode(rowIndex, 0).focus();},200);
+                    }
+                });
+                dialog.editPane.fieldOrder = this.fieldOrder;
+                dialog.editPane.mode = 'update';
+                dialog.startup();
+                dialog.show();
+            },
+
+            showCreateDialog : function() {
+                var grid = this;
+                var dialog = new openils.widget.EditDialog({
+                    fmClass : this.fmClass,
+                    overrideWidgets : this.overrideEditWidgets,
+                    onPostSubmit : function(r) {
+                        var fmObject = openils.Util.readResponse(r);
+                        if(fmObject) 
+                            grid.store.newItem(fmObject.toStoreItem());
+                        dialog.destroy();
+                        if(grid.onPostCreate)
+                            grid.onPostCreate(fmObject);
+                        setTimeout(function(){
+                            try {
+                                grid.selection.select(grid.rowCount-1);
+                                grid.views.views[0].getCellNode(grid.rowCount-1, 1).focus();
+                            } catch (E) {}
+                        },200);
+                    },
+                });
+                dialog.editPane.fieldOrder = this.fieldOrder;
+                dialog.editPane.mode = 'create';
+                dialog.startup();
+                dialog.show();
+            },
+            
+            resetStore : function() {
                 this.setStore(this.buildAutoStore());
             },
 
-        }
+            loadAll : function(opts, search) {
+                dojo.require('openils.PermaCrud');
+                if(!opts) opts = {};
+                var self = this;
+                opts = dojo.mixin(opts, {
+                    async : true,
+                    streaming : true,
+                    onresponse : function(r) {
+                        var item = openils.Util.readResponse(r);
+                        self.store.newItem(item.toStoreItem());
+                    }
+                });
+                if(search)
+                    new openils.PermaCrud().search(this.fmClass, search, opts);
+                else
+                    new openils.PermaCrud().retrieveAll(this.fmClass, opts);
+            }
+        } 
     );
     openils.widget.AutoGrid.markupFactory = dojox.grid.DataGrid.markupFactory;
 

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditDialog.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditDialog.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditDialog.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -27,8 +27,18 @@
             constructor : function(args) {
                 this.editPane = new openils.widget.EditPane(args);
                 var self = this;
-                this.editPane.onCancel = function() { self.hide(); }
-                this.editPane.onPostApply = function() { self.hide(); }
+
+                this.editPane.onCancel = function() { 
+                    if(args.onCancel)
+                        args.onCancel();
+                    self.hide(); 
+                }
+
+                this.editPane.onPostSubmit = function(r) { 
+                    self.hide(); 
+                    if(args.onPostSubmit)
+                        args.onPostSubmit(r);
+                }
             },
 
             /**

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditPane.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditPane.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/EditPane.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -12,12 +12,12 @@
         [dijit.layout.ContentPane, openils.widget.AutoWidget],
         {
             mode : 'update',
-            fieldList : [], // holds the field name + associated widget
-            onPostApply : null, // apply callback
+            onPostSubmit : null, // apply callback
             onCancel : null, // cancel callback
             hideActionButtons : false,
 
             constructor : function(args) {
+                this.fieldList = [];
                 for(var k in args)
                     this[k] = args[k];
             },
@@ -29,6 +29,7 @@
             startup : function() {
                 this.inherited(arguments);
                 this.initAutoEnv();
+                this.hideActionButtons = this.readOnly;
 
                 var table = document.createElement('table');
                 var tbody = document.createElement('tbody');
@@ -39,13 +40,21 @@
                 if(this.fmIDL.permacrud && this.fmIDL.permacrud[this.mode])
                     this.limitPerms = this.fmIDL.permacrud[this.mode].perms;
 
+                if(!this.overrideWidgets)
+                    this.overrideWidgets = {};
+
                 for(var f in this.sortedFieldList) {
                     var field = this.sortedFieldList[f];
                     if(!field || field.virtual) continue;
 
+                    if(field.name == this.fmIDL.pkey && this.mode == 'create' && this.fmIDL.pkey_sequence)
+                        continue; /* don't show auto-generated fields on create */
+
                     var row = document.createElement('tr');
                     var nameTd = document.createElement('td');
                     var valTd = document.createElement('td');
+                    var valSpan = document.createElement('span');
+                    valTd.appendChild(valSpan);
 
                     nameTd.appendChild(document.createTextNode(field.label));
                     row.appendChild(nameTd);
@@ -55,11 +64,16 @@
                     var widget = new openils.widget.AutoFieldWidget({
                         idlField : field, 
                         fmObject : this.fmObject,
-                        parentNode : valTd,
-                        orgLimitPerms : this.limitPerms
+                        fmClass : this.fmClass,
+                        parentNode : valSpan,
+                        orgLimitPerms : this.limitPerms,
+                        readOnly : this.readOnly,
+                        widget : this.overrideWidgets[field.name]
                     });
+
                     widget.build();
                     this.fieldList.push({name:field.name, widget:widget});
+                    //this.applySaveOnEnter(widget);
                 }
                 if(!this.hideActionButtons)
                     this.buildActionButtons(tbody);
@@ -67,31 +81,38 @@
                 openils.Util.addCSSClass(table, 'oils-fm-edit-dialog');
             },
 
+            applySaveOnEnter : function(widget) {
+                var self = this;
+                dojo.connect(this, 'onKeyDown',
+                    function(e) {
+                        if(e.keyCode == dojo.keys.ENTER) 
+                            self.performAutoEditAction();
+                    }
+                );
+            },
+
             buildActionButtons : function(tbody) {
                 var row = document.createElement('tr');
                 var cancelTd = document.createElement('td');
                 var applyTd = document.createElement('td');
+                var cancelSpan = document.createElement('span');
+                var applySpan = document.createElement('span');
                 row.appendChild(cancelTd);
                 row.appendChild(applyTd);
+                cancelTd.appendChild(cancelSpan);
+                applyTd.appendChild(applySpan);
                 tbody.appendChild(row);
 
                 var self = this;
                 new dijit.form.Button({
                     label:'Cancel', // XXX
                     onClick : this.onCancel
-                }, cancelTd);
+                }, cancelSpan);
 
                 new dijit.form.Button({
                     label:'Save',  // XXX
-                    onClick: function() {
-                        self.performEditAction({
-                            oncomplete:function() {
-                                if(self.onPostApply)
-                                    self.onPostApply();
-                            }
-                        });
-                    }
-                }, applyTd);
+                    onClick: function() {self.performAutoEditAction();}
+                }, applySpan);
             },
 
             getFields : function() {
@@ -105,13 +126,25 @@
                 }
             },
 
+            performAutoEditAction : function() {
+                var self = this;
+                self.performEditAction({
+                    oncomplete:function(r) {
+                        if(self.onPostSubmit)
+                            self.onPostSubmit(r);
+                    }
+                });
+            },
+
             performEditAction : function(opts) {
                 var pcrud = new openils.PermaCrud();
                 var fields = this.getFields();
                 if(this.mode == 'create')
                     this.fmObject = new fieldmapper[this.fmClass]();
-                for(var idx in fields) 
+                for(var idx in fields)  
                     this.fmObject[fields[idx]](this.getFieldValue(fields[idx]));
+                if(this.mode == 'create' && this.fmIDL.pkey_sequence)
+                    this.fmObject[this.fmIDL.pkey](null);
                 pcrud[this.mode](this.fmObject, opts);
             }
         }

Modified: branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/FilteringTreeSelect.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/FilteringTreeSelect.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/dojo/openils/widget/FilteringTreeSelect.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -25,7 +25,6 @@
             labelAttr : 'name',
             childField : 'children',
             tree : null,
-            dataList : [],
 
             startup : function() {
                 this.tree = (typeof this.tree == 'string') ? 
@@ -35,10 +34,13 @@
                     return;
                 }
                 if(!dojo.isArray(this.tree)) this.tree = [this.tree];
+                this.dataList = [];
                 var self = this;
-                this.tree.forEach(function(node) { self._makeNodeList(node); });
-                this.store = new dojo.data.ItemFileReadStore(
-                    {data:fieldmapper[this.dataList[0].classname].toStoreData(this.dataList)});
+                dojo.forEach(this.tree, function(node) { self._makeNodeList(node); });
+                if(this.dataList.length > 0) {
+                    this.store = new dojo.data.ItemFileReadStore(
+                        {data:fieldmapper[this.dataList[0].classname].toStoreData(this.dataList)});
+                }
                 this.inherited(arguments);
             },
 

Deleted: branches/staff-client-experiment/Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -1,49 +0,0 @@
-dojo.require("dijit.Dialog");
-dojo.require('dijit.form.Button');
-dojo.require('dojox.grid.DataGrid');
-dojo.require('dojo.data.ItemFileWriteStore');
-dojo.require('openils.acq.CurrencyType');
-dojo.require('openils.Event');
-dojo.require('openils.Util');
-dojo.require('fieldmapper.dojoData');
-
-var currencyTypes = [];
-
-function loadCTypesGrid() {
-    var store = new dojo.data.ItemFileWriteStore({data:acqct.initStoreData('code', {identifier:'code'})});
-    currencyTypeListGrid.setStore(store);
-    currencyTypeListGrid.render();
-
-    fieldmapper.standardRequest(
-        [ 'open-ils.acq', 'open-ils.acq.currency_type.all.retrieve'],
-        { async: true,
-          params: [openils.User.authtoken],
-          onresponse : function(r){
-                if(ct = openils.Util.readResponse(r)) {
-                    openils.acq.CurrencyType.cache[ct.code()] = ct;
-                    store.newItem(acqct.toStoreItem(ct));
-                }
-            }
-        }
-    );
-}
-
-function createCT(args) {
-    if(!(args.code && args.label)) return;
-    var ct = new acqct();
-    ct.code(args.code);
-    ct.label(args.label);
-    fieldmapper.standardRequest(
-        ['open-ils.permacrud', 'open-ils.permacrud.create.acqct'],
-        {   async: true,
-            params: [openils.User.authtoken, ct],
-            oncomplete: function(r) {
-                if(new String(openils.Util.readResponse(r)) != '0')
-                    loadCTypesGrid();
-            }
-        }
-    );
-}
-
-
-openils.Util.addOnLoad(loadCTypesGrid);

Copied: branches/staff-client-experiment/Open-ILS/web/js/ui/default/actor (from rev 12227, trunk/Open-ILS/web/js/ui/default/actor)

Copied: branches/staff-client-experiment/Open-ILS/web/js/ui/default/conify/global/acq (from rev 12227, trunk/Open-ILS/web/js/ui/default/conify/global/acq)

Copied: branches/staff-client-experiment/Open-ILS/web/js/ui/default/conify/global/config/idl_field_doc.js (from rev 12227, trunk/Open-ILS/web/js/ui/default/conify/global/config/idl_field_doc.js)
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/js/ui/default/conify/global/config/idl_field_doc.js	                        (rev 0)
+++ branches/staff-client-experiment/Open-ILS/web/js/ui/default/conify/global/config/idl_field_doc.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -0,0 +1,50 @@
+dojo.require('dijit.form.FilteringSelect');
+dojo.require('dojo.data.ItemFileReadStore');
+dojo.require('fieldmapper.IDL');
+dojo.require('openils.PermaCrud');
+dojo.require('openils.widget.AutoGrid');
+
+function updateFieldSelector() {
+    var cls = this.attr('value');
+    if(!cls) return;
+    var flist = fieldmapper.IDL.fmclasses[cls];
+    var fields = [];
+    for(var f in flist.fields) {
+        var field = flist.fields[f];
+        if(field.virtual) continue;
+        fields.push({name:field.label, value:field.name});
+    }
+    fdocGrid.overrideEditWidgets.field.store = new dojo.data.ItemFileReadStore(
+        {data:{identifier:'value', label:'name', items:fields}});
+}
+
+function load() {
+    var slist = fieldmapper.IDL.fmclasses;
+    var dlist = [];
+
+    fdocGrid.overrideEditWidgets.field = editFieldSelector;
+    fdocGrid.overrideEditWidgets.fm_class = editClassSelector;
+    dojo.connect(fdocGrid.overrideEditWidgets.fm_class, 'onChange', updateFieldSelector);
+
+    for(var f in slist) {
+        if(slist[f].label != slist[f].name) // only show tables that have an actual label
+            dlist.push({value:slist[f].name, name:slist[f].label});
+    }
+    dlist = dlist.sort(function(a, b){return (a.name < b.name) ? -1 : 1;});
+
+    fmClassSelector.store = 
+        fdocGrid.overrideEditWidgets.fm_class.store = 
+            new dojo.data.ItemFileReadStore({data:{identifier:'value', label:'name', items:dlist}});
+
+    fmClassSelector.startup();
+    dojo.connect(fmClassSelector, 'onChange',
+        function() {
+            fdocGrid.resetStore();
+            fdocGrid.loadAll({order_by:{fdoc : 'field'}}, {fm_class: this.attr('value')});
+        }
+    );
+}
+
+
+openils.Util.addOnLoad(load);
+

Modified: branches/staff-client-experiment/Open-ILS/web/opac/common/js/RemoteRequest.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/opac/common/js/RemoteRequest.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/opac/common/js/RemoteRequest.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -61,7 +61,8 @@
 
 	/* dojo is currently only available in the OPAC */
 	try {
-		this.locale	= dojo.config.locale;
+		/* We want OpenSRF.locale for xx-YY format */
+		this.locale	= OpenSRF.locale;
 	}
 	catch (e) {
 		this.locale = null;

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/Thumbs.db (from rev 12227, trunk/Open-ILS/web/opac/images/Thumbs.db)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/advancedsearch-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/advancedsearch-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/advancedsearch-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/advancedsearch-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/blank.gif (from rev 12227, trunk/Open-ILS/web/opac/images/blank.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/book-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/book-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/cancel-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/cancel-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/cancel-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/cancel-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/cancel-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/cartographic.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/cartographic.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/chooselibrary-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/chooselibrary-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/chooselibrary-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/chooselibrary-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/closeall-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/closeall-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/closeall-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/closeall-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/closeall-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/content-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/content-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/content-bg.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/content-bg.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/details-f-bg-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/details-f-bg-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/details-f-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/details-f-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/details-headers-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/details-headers-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/earth-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/earth-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/eg_tiny_logo.gif (from rev 12227, trunk/Open-ILS/web/opac/images/eg_tiny_logo.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/expandall-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/expandall-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/expandall-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/expandall-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/expandall-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-bl.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-bl.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-bottom.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-bottom.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-br.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-br.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-corners.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-corners.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-left.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-left.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-right.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-right.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-tl.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-tl.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-top.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-top.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/footer-tr.gif (from rev 12227, trunk/Open-ILS/web/opac/images/footer-tr.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/header-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/header-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/header-shadow.gif (from rev 12227, trunk/Open-ILS/web/opac/images/header-shadow.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/home-bottom-tag-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/home-bottom-tag-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/home-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/home-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/home-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/home-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/home-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/inner-account-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/inner-account-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/inner-account-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-account-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/inner-account-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/inner-advanced-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/inner-advanced-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/inner-advanced-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/inner-advanced-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/inner-advanced-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/lg-txt.gif (from rev 12227, trunk/Open-ILS/web/opac/images/lg-txt.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/libselect-btn.gif (from rev 12227, trunk/Open-ILS/web/opac/images/libselect-btn.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/list-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/list-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/list-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/list-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/login-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/login-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/login-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/login-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/login-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/loginas-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/loginas-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/loginas-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/loginas-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/logo.gif (from rev 12227, trunk/Open-ILS/web/opac/images/logo.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/logo.png (from rev 12227, trunk/Open-ILS/web/opac/images/logo.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/logout-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/logout-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/logout-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/logout-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/logout-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/mix-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/mix-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/mixed material.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/mixed material.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/mov-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/mov-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/moving image.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/moving image.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/mussymbol-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/mussymbol-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/myaccount-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/myaccount-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/myaccount-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/myaccount-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/noimg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/noimg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/nonmusic-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/nonmusic-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/notated music.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/notated music.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/pic-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/pic-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/placeholder-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/placeholder-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/progressbar_green-old.gif (from rev 12227, trunk/Open-ILS/web/opac/images/progressbar_green-old.gif)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/progressbar_green.gif
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/recsound-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/recsound-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/reg-txt.gif (from rev 12227, trunk/Open-ILS/web/opac/images/reg-txt.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/relevant-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/relevant-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/relevant-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/relevant-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/relevant-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/search-btn.gif (from rev 12227, trunk/Open-ILS/web/opac/images/search-btn.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/searchbar-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/searchbar-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/searchbox-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/searchbox-bg.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/series-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/series-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/series-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/series-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/series-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/sidebar-bg.gif (from rev 12227, trunk/Open-ILS/web/opac/images/sidebar-bg.gif)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/slimtree/folder2.gif
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/slimtree/folderopen2.gif
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/small-rss.gif (from rev 12227, trunk/Open-ILS/web/opac/images/small-rss.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/software, multimedia.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/software, multimedia.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/software-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/software-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/sound recording-musical.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/sound recording-musical.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/sound recording-nonmusical.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/sound recording-nonmusical.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/sound recording.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/sound recording.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/sound-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/sound-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/still images.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/still images.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/subject-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/subject-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/subject-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/subject-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/text.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/text.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/three dimensional object.jpg (from rev 12227, trunk/Open-ILS/web/opac/images/three dimensional object.jpg)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/threed-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/threed-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/title-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/title-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/title-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/title-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/title-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon-u.gif (from rev 12227, trunk/Open-ILS/web/opac/images/titledetails-icon-u.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon-u.png (from rev 12227, trunk/Open-ILS/web/opac/images/titledetails-icon-u.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/titledetails-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/titledetails-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/titledetails-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/book-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/book-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/book-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/book-icon.png)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/cartographic.jpg
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/earth-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/earth-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/earth-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/earth-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mix-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/mix-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mix-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/mix-icon.png)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mixed material.jpg
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mov-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/mov-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mov-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/mov-icon.png)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/moving image.jpg
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mussymbol-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/mussymbol-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/mussymbol-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/mussymbol-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/nonmusic-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/nonmusic-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/nonmusic-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/nonmusic-icon.png)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/notated music.jpg
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/pic-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/pic-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/pic-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/pic-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/placeholder-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/placeholder-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/placeholder-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/placeholder-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/recsound-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/recsound-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/recsound-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/recsound-icon.png)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/software, multimedia.jpg
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/software-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/software-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/software-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/software-icon.png)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound recording-musical.jpg
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound recording-nonmusical.jpg
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound recording.jpg
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/sound-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/sound-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/sound-icon.png)
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/still images.jpg
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/text.jpg
===================================================================
(Binary files differ)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/three dimensional object.jpg
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/threed-icon.gif (from rev 12227, trunk/Open-ILS/web/opac/images/tor/threed-icon.gif)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/images/tor/threed-icon.png (from rev 12227, trunk/Open-ILS/web/opac/images/tor/threed-icon.png)
===================================================================
(Binary files differ)

Copied: branches/staff-client-experiment/Open-ILS/web/opac/skin/craftsman (from rev 12227, trunk/Open-ILS/web/opac/skin/craftsman)

Modified: branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/holds.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/holds.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/holds.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -842,7 +842,7 @@
 
 function holdsCancel(holdid, user) {
 	if(!user) user = G.user;
-	var req = new Request(CANCEL_HOLD, user.session, holdid);
+	var req = new Request(CANCEL_HOLD, user.session, holdid, /* Patron via OPAC */ 6);
 	req.send(true);
 	return req.result();
 	runEvt('common', 'holdUpdated');

Modified: branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/myopac.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/myopac.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/opac/skin/default/js/myopac.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -921,6 +921,7 @@
         repl.address_type(addr.address_type());
         repl.within_city_limits(addr.within_city_limits());
         repl.replaces(addr.id());
+        repl.pending('t');
         repl.isnew(true);
         repl.id(null);
         addr = repl;

Modified: branches/staff-client-experiment/Open-ILS/web/opac/skin/default/xml/common/js_common.xml
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/opac/skin/default/xml/common/js_common.xml	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/opac/skin/default/xml/common/js_common.xml	2009-02-19 05:39:53 UTC (rev 12228)
@@ -41,7 +41,7 @@
         var locale = location.href.replace( /.+opac\/([^\/]+)\/skin.+/, '$1' );
         if (!locale) locale = '<!--#echo var="locale"-->';
 
-        djConfig.locale = locale;
+        djConfig.locale = locale.toLowerCase();
 
     </script>
 

Copied: branches/staff-client-experiment/Open-ILS/web/opac/theme/craftsman (from rev 12227, trunk/Open-ILS/web/opac/theme/craftsman)

Modified: branches/staff-client-experiment/Open-ILS/web/templates/base.tt2
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/templates/base.tt2	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/templates/base.tt2	2009-02-19 05:39:53 UTC (rev 12228)
@@ -1,6 +1,7 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns='http://www.w3.org/1999/xhtml' lang='${locale}' xml:lang='${locale}'>
+<html xmlns='http://www.w3.org/1999/xhtml' lang='[% ctx.locale %]' xml:lang='[% ctx.locale %]'>
     <head>
+        <title>[% ctx.page_title %]</title>
         <link rel='stylesheet' type='text/css' 
             href='[% ctx.media_prefix %]/css/skin/[% ctx.skin %].css'></link>
         <link rel='stylesheet' type='text/css' 

Deleted: branches/staff-client-experiment/Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2	2009-02-19 05:39:53 UTC (rev 12228)
@@ -1,52 +0,0 @@
-[% WRAPPER 'default/base.tt2' %]
-<div id='oils-acq-list-header' class='container'>
-    <div id='oils-acq-list-header-label'>Currency Types</div>
-</div>
-
-<!-- load the page-specific JS -->
-<script src='[% ctx.media_prefix %]/js/ui/default/acq/financial/list_currency_types.js'> </script>
-
-<div class='oils-acq-actions-div'>
-    <div dojoType="dijit.form.DropDownButton">
-        <!-- TODO: add perm and disable button if necessary XXX -->
-        <span>New Currency Type</span>
-        <div dojoType="dijit.TooltipDialog" execute="createCT(arguments[0]);">
-            <table class='dijitTooltipTable'>
-                <tr>
-                    <td><label for="label">Label: </label></td>
-                    <td><input dojoType="dijit.form.TextBox" name="label"/></td>
-                </tr>
-                <tr>
-                    <td><label for="code">Code: </label></td>
-                    <td><input dojoType="dijit.form.TextBox" name="code"/></td>
-                </tr>
-                <tr>
-                    <td colspan='2' align='center'>
-                        <button dojoType=dijit.form.Button type="submit">Create</button>
-                    </td>
-                </tr>
-            </table>
-        </div>
-    </div> 
-
-    <button dojoType="dijit.form.Button" onclick="deleteSelectedCT()">
-        Delete Selected
-    </button>
-</div>
-
-<!-- The main grid lives here -->
-</script> 
-<div dojoType="dijit.layout.ContentPane" layoutAlign="top"> 
-    <div dojoType="dijit.layout.ContentPane" layoutAlign="client" style='height:600px;'> 
-        <table jsId="currencyTypeListGrid" dojoType="dojox.grid.DataGrid" query="{code: '*'}" rowSelector='20px'> 
-            <thead> 
-                <tr> 
-                    <th field="code">Code</th> 
-                    <th field="label" width='auto'>Label</th> 
-                </tr> 
-            </thead> 
-        </table>     
-    </div> 
-</div> 
-
-[% END %]

Copied: branches/staff-client-experiment/Open-ILS/web/templates/default/actor (from rev 12227, trunk/Open-ILS/web/templates/default/actor)

Copied: branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/acq (from rev 12227, trunk/Open-ILS/web/templates/default/conify/global/acq)

Copied: branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/action_trigger (from rev 12227, trunk/Open-ILS/web/templates/default/conify/global/action_trigger)

Copied: branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/config/idl_field_doc.tt2 (from rev 12227, trunk/Open-ILS/web/templates/default/conify/global/config/idl_field_doc.tt2)
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/config/idl_field_doc.tt2	                        (rev 0)
+++ branches/staff-client-experiment/Open-ILS/web/templates/default/conify/global/config/idl_field_doc.tt2	2009-02-19 05:39:53 UTC (rev 12228)
@@ -0,0 +1,29 @@
+[% WRAPPER default/base.tt2 %]
+<script src='[% ctx.media_prefix %]/js/ui/default/conify/global/config/idl_field_doc.js'> </script>
+<table style='width:100%;margin-bottom:10px;'>
+    <tr>
+        <td align='left'><h3>Field Documentation</h3></td>
+        <td align='right'>
+            <span dojoType='dijit.form.FilteringSelect' jsId='fmClassSelector'></span>
+            <button dojoType='dijit.form.Button' onClick='fdocGrid.showCreateDialog()'>New</button>
+        </td>
+    </tr>
+</table>
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client" style='height:90%'>
+    <table  jsId="fdocGrid" 
+            dojoType="openils.widget.AutoGrid" 
+            fieldOrder="['id', 'fm_class', 'field', 'owner', 'string']"
+            query="{id: '*'}" 
+            fmClass='fdoc'
+            defaultCellWidth='12'
+            editOnEnter='true'>
+        <thead>
+            <tr><th field='string' width='auto'/></tr>
+        </thead>
+    </table>
+</div>
+<div class='hidden'>
+    <span dojoType='dijit.form.FilteringSelect' jsId='editClassSelector'></span>
+    <span dojoType='dijit.form.FilteringSelect' jsId='editFieldSelector'></span>
+</div>
+[% END %]

Modified: branches/staff-client-experiment/Open-ILS/web/templates/default/menu.tt2
===================================================================
--- branches/staff-client-experiment/Open-ILS/web/templates/default/menu.tt2	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/web/templates/default/menu.tt2	2009-02-19 05:39:53 UTC (rev 12228)
@@ -68,20 +68,41 @@
         <div dojoType="dijit.form.DropDownButton">
             <span>Admin</span>
             <div dojoType="dijit.Menu">
-                <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
-                        onClick="location.href = '[% ctx.base_uri %]/acq/fund/list';">Funds</div>
-                <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
-                        onClick="location.href = '[% ctx.base_uri %]/acq/funding_source/list';">Funding Sources</div>
-                <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
-                        onClick="location.href = '[% ctx.base_uri %]/acq/provider/list';">Providers</div>
-                <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
-                        onClick="location.href = '[% ctx.base_uri %]/acq/currency_type/list';">Currency Types</div>
-                <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
-                        onClick="location.href = '[% ctx.base_uri %]/conify/global/config/billing_type';">Billing Types</div>
-                <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
-                        onClick="location.href = '[% ctx.base_uri %]/conify/global/config/standing_penalty';">Standing Penalties</div>
-                <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
-                        onClick="location.href = '[% ctx.base_uri %]/conify/global/permission/grp_penalty_threshold';">Group Penalty Thresholds</div>
+                <div dojoType="dijit.PopupMenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy">
+                    <span>Acquisitions</span>
+                     <div dojoType="dijit.Menu">
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy"
+                            onClick="location.href = '[% ctx.base_uri %]/acq/fund/list';">Funds</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/acq/funding_source/list';">Funding Sources</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/acq/provider';">Providers</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/acq/currency_type/list';">Currency Types</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/acq/exchange_rate';">Exchange Rates</div>
+                    </div>
+                </div>
+                <div dojoType="dijit.PopupMenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy">
+                    <span>Config</span>
+                     <div dojoType="dijit.Menu">
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/config/billing_type';">Billing Types</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/config/standing_penalty';">Standing Penalties</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/permission/grp_penalty_threshold';">
+                                    Group Penalty Thresholds</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/config/idl_field_doc';">Field Documentation</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/config/z3950_source';">Z39.50 Sources</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/config/circ_modifier';">Circulation Modifiers</div>
+                        <div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy" 
+                                onClick="location.href = '[% ctx.base_uri %]/conify/global/action_trigger/event_definition';">Action Trigger</div>
+                    </div>
+                </div>
             </div>
         </div>
     </div>

Modified: branches/staff-client-experiment/Open-ILS/xul/staff_client/chrome/content/main/about.html
===================================================================
--- branches/staff-client-experiment/Open-ILS/xul/staff_client/chrome/content/main/about.html	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/xul/staff_client/chrome/content/main/about.html	2009-02-19 05:39:53 UTC (rev 12228)
@@ -8,7 +8,7 @@
 Evergreen is a library automation software product. It assists libraries in day-to-day operations such as checking out materials, keeping track of patrons, and providing a web-based library catalog. The majority of the workings of Evergreen are behind the scenes, and users (or patrons) rarely see anything behind the web catalog. The library staff, on the other hand, spend much of their day utilizing the software in order to do their jobs efficiently and effectively.
 </p>
 <p>
-Evergreen calculates due dates and loan periods. It determines how many renewals a user may have. It keeps track of where all the books, CD's, DVD's, and other materials a library may own is located and their condition. The Evergreen ILS has a user-friendly web catalog that allows patrons to find what they are looking for. It does all of that, and even more. 
+Evergreen calculates due dates and loan periods. It determines how many renewals a user may have. It keeps track of where all the books, CD's, DVD's, and other materials a library may own are located and their condition. The Evergreen ILS has a user-friendly web catalog that allows patrons to find what they are looking for. It does all of that, and even more. 
 </p>
 <p>Evergreen is Copyright &#0169; Georgia Public Library Service - A Unit of the University System of Georgia, and others. The Evergreen software is distributed under the GNU General Public License, Version 2.</p>
 </blockquote>
@@ -29,7 +29,7 @@
 <h2>The Translation Team:</h2>
 <ul>
 <li><b>Vaclav Jansa, Linda Skolkova, and Anna Stocklova</b>, <a href="http://uisk.ff.cuni.cz/">Institute of Information Studies and Librarianship, Faculty of Arts, Charles University, Prague</a>, cs-CZ</br/></li>
-<li><b>John Fink</b> (<a href="http://mcmaster.ca">McMaster University</a>) and Warren Layton (<a href="http://thebookpile.wordpress.com/">)</b>, en-CA<br/></li>
+<li><b>John Fink</b> (<a href="http://mcmaster.ca">McMaster University</a>) and <b><a href="http://thebookpile.wordpress.com/">Warren Layton</a></b>, en-CA<br/></li>
 <li><b><a href="http://nrcan.gc.ca/">Natural Resources Canada</a></b>, fr-CA<br/></li>
 <li><b>Tigran Zargaryan</b>, <a href="http://www.flib.sci.am">Fundamental Scientific Library of the National Academy of Sciences of the Republic of Armenia</a>, hy-AM<br/></li>
 </ul>

Modified: branches/staff-client-experiment/Open-ILS/xul/staff_client/server/cat/marcedit.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/xul/staff_client/server/cat/marcedit.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/xul/staff_client/server/cat/marcedit.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -130,18 +130,26 @@
 
 		// Try to get the locale from our preferences
 		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-        try {
-            const Cc = Components.classes;
-            const Ci = Components.interfaces;
-            locale = Cc["@mozilla.org/preferences-service;1"].
+		try {
+			const Cc = Components.classes;
+			const Ci = Components.interfaces;
+			locale = Cc["@mozilla.org/preferences-service;1"].
 				getService(Ci.nsIPrefBranch).
 				getCharPref("general.useragent.locale");
-        }
+		}
 		catch (e) { }
 
+		// TODO: We should send a HEAD request to check for the existence of the desired file
+		// then fall back to the default locale if preferred locale is not necessary;
+		// however, for now we have a simplistic check:
+		//
+		// we currently have translations for only two locales; in the absence of a
+		// valid locale, default to the almighty en-US
+		if (locale != 'en-US' && locale != 'fr-CA') {
+			locale = 'en-US';
+		}
+
 		// Get the locale-specific tooltips
-		// TODO: We should send a HEAD request to check for the existence of the desired file
-		// then fall back to the default locale if preferred locale is not necessary
 		req.open('GET','/xul/server/locale/' + locale + '/marcedit-tooltips.xml',true);
 
 		context_menus = createComplexXULElement('popupset');

Modified: branches/staff-client-experiment/Open-ILS/xul/staff_client/server/patron/ue_config.js
===================================================================
--- branches/staff-client-experiment/Open-ILS/xul/staff_client/server/patron/ue_config.js	2009-02-19 02:12:46 UTC (rev 12227)
+++ branches/staff-client-experiment/Open-ILS/xul/staff_client/server/patron/ue_config.js	2009-02-19 05:39:53 UTC (rev 12228)
@@ -646,6 +646,7 @@
                 // update the ID on the new address
                 address.id(oldId);
                 address.replaces(null);
+                address.pending('f');
                 removeChildren($('ue_address_tbody'));
 	            uEditBuildAddrs(patron);
             }
@@ -697,7 +698,7 @@
 	uEditCheckSharedAddr(patron, address, tbody, row);
     
     // see if this is a pending address
-    if( address.replaces() != null ) {
+    if( isTrue(address.pending()) ) {
         var button = $n(row, 'ue_addr_approve');
         unHideMe(button);
         button.onclick = function() { uEditApproveAddr( tbody, row, address ); }



More information about the open-ils-commits mailing list