[open-ils-commits] r18372 - in branches/serials-integration: . Open-ILS/examples Open-ILS/examples/apache Open-ILS/src/c-apps Open-ILS/src/edi_translator Open-ILS/src/perlmods/OpenILS Open-ILS/src/perlmods/OpenILS/Application Open-ILS/src/perlmods/OpenILS/Application/Acq Open-ILS/src/perlmods/OpenILS/Application/Circ Open-ILS/src/perlmods/OpenILS/Application/Search Open-ILS/src/perlmods/OpenILS/Application/Storage 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/SIP Open-ILS/src/perlmods/OpenILS/SIP/Transaction Open-ILS/src/perlmods/OpenILS/Utils Open-ILS/src/perlmods/OpenILS/WWW Open-ILS/src/reporter Open-ILS/src/sql/Pg Open-ILS/src/sql/Pg/upgrade Open-ILS/src/support-scripts Open-ILS/src/templates/password-reset Open-ILS/web/conify/global/permission Open-ILS/web/js/dojo/MARC Open-ILS/web/js/dojo/openils Open-ILS/web/js/dojo/openils/actor/nls Open-ILS/web/js/dojo/openils/widget Open-ILS/web/js/ui Open-ILS/web/js/ui/default/actor/user Open-ILS/web/js/ui/default/cat/authority Open-ILS/web/js/ui/default/circ/selfcheck Open-ILS/web/js/ui/default/conify/global/action_trigger Open-ILS/web/js/ui/default/conify/global/asset Open-ILS/web/js/ui/default/vandelay Open-ILS/web/opac/common/js Open-ILS/web/opac/extras Open-ILS/web/opac/locale/en-US Open-ILS/web/opac/skin/default/xml/rdetail Open-ILS/web/templates Open-ILS/web/templates/default/acq/po Open-ILS/web/templates/default/actor/user Open-ILS/web/templates/default/circ/selfcheck Open-ILS/web/templates/default/conify/global/action_trigger Open-ILS/web/templates/default/conify/global/config Open-ILS/web/templates/default/vandelay/inc Open-ILS/xul/staff_client/chrome/content/OpenILS Open-ILS/xul/staff_client/chrome/content/auth Open-ILS/xul/staff_client/chrome/content/cat Open-ILS/xul/staff_client/chrome/content/circ Open-ILS/xul/staff_client/chrome/content/main Open-ILS/xul/staff_client/chrome/content/util Open-ILS/xul/staff_client/chrome/locale/en-US Open-ILS/xul/staff_client/components Open-ILS/xul/staff_client/external Open-ILS/xul/staff_client/server/admin Open-ILS/xul/staff_client/server/cat Open-ILS/xul/staff_client/server/circ Open-ILS/xul/staff_client/server/locale/en-US Open-ILS/xul/staff_client/server/main Open-ILS/xul/staff_client/server/patron Open-ILS/xul/staff_client/server/serial Open-ILS/xul/staff_client/server/skin build/tools (dbwells)

svn at svn.open-ils.org svn at svn.open-ils.org
Mon Oct 18 10:05:10 EDT 2010


Author: dbwells
Date: 2010-10-18 10:05:06 -0400 (Mon, 18 Oct 2010)
New Revision: 18372

Added:
   branches/serials-integration/Open-ILS/src/edi_translator/install.RHEL.sh
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0414.schema.call-number-upd-ins-trigger.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0415.schema.rename-field-class-fkey.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0416.schema.rename-recuring-idx.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0417.schema.acq.drop-lineitem-item-count.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0418.function.action.fix-purge-circ.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0419.schema.hold_record_view.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0420.schema.premunge_dates.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0421.schema.embiggen-ints.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0422.schema.acq.lineitem-history-bigint.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0423.schema.support-null-function-in-xpath_table.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0424.schema.circ_due_date_trigger.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0425.schema.perm-grp-tree-hold-priority.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0426.data.perm-list.misc-acq.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0427.schema.hold_matrix_root_ou.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0428.schema.hold_retarget.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0429.noop-because-miker-skipped-it.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0430.schema.strict_ou_test.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0431.schema.hold_retarget.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0432.schema.config_hard_due_date.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0433.edi_orders_template.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0435.schema.template-add-field.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0436.schema.multiple-rules.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0437.schema.bytea-index-label_sortkey.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0438.schema.bytea-index-label.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0439.schema.function-bytea-index-label.sql
   branches/serials-integration/Open-ILS/src/templates/password-reset/strings.fr-CA
   branches/serials-integration/Open-ILS/web/opac/extras/circ/
   branches/serials-integration/Open-ILS/xul/staff_client/components/clh.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_overlay_vertical.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_vertical.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/skin/custom.js
Modified:
   branches/serials-integration/
   branches/serials-integration/Open-ILS/examples/apache/eg.conf
   branches/serials-integration/Open-ILS/examples/apache/eg_vhost.conf
   branches/serials-integration/Open-ILS/examples/fm_IDL.xml
   branches/serials-integration/Open-ILS/examples/oils_sip.xml.example
   branches/serials-integration/Open-ILS/examples/opensrf.xml.example
   branches/serials-integration/Open-ILS/examples/remoteauth.cgi
   branches/serials-integration/Open-ILS/src/c-apps/oils_sql.c
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/CopyLocations.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Search/Biblio.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/asset.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/permission.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/serial.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/asset.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/authority.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/QueryParser.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Reactor.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Vandelay.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Item.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Patron.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Transaction/Checkin.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/Penalty.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/PermitHold.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/RemoteAccount.pm
   branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/Proxy.pm
   branches/serials-integration/Open-ILS/src/reporter/clark-kent.pl
   branches/serials-integration/Open-ILS/src/sql/Pg/002.functions.config.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/002.schema.config.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/006.schema.permissions.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/030.schema.metabib.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/040.schema.asset.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/070.schema.container.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/090.schema.action.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/095.schema.booking.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/110.hold_matrix.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/200.schema.acq.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/950.data.seed-values.sql
   branches/serials-integration/Open-ILS/src/sql/Pg/reporter-schema.sql
   branches/serials-integration/Open-ILS/src/support-scripts/action_trigger_runner.pl
   branches/serials-integration/Open-ILS/src/support-scripts/edi_fetcher.pl
   branches/serials-integration/Open-ILS/src/support-scripts/edi_pusher.pl
   branches/serials-integration/Open-ILS/src/support-scripts/marc_export
   branches/serials-integration/Open-ILS/web/conify/global/permission/grp_tree.html
   branches/serials-integration/Open-ILS/web/js/dojo/MARC/Field.js
   branches/serials-integration/Open-ILS/web/js/dojo/MARC/Record.js
   branches/serials-integration/Open-ILS/web/js/dojo/openils/BibTemplate.js
   branches/serials-integration/Open-ILS/web/js/dojo/openils/PermaCrud.js
   branches/serials-integration/Open-ILS/web/js/dojo/openils/XUL.js
   branches/serials-integration/Open-ILS/web/js/dojo/openils/actor/nls/register.js
   branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js
   branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js
   branches/serials-integration/Open-ILS/web/js/ui/base.js
   branches/serials-integration/Open-ILS/web/js/ui/default/actor/user/register.js
   branches/serials-integration/Open-ILS/web/js/ui/default/cat/authority/list.js
   branches/serials-integration/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
   branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/action_trigger/event_definition.js
   branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/asset/copy_location_order.js
   branches/serials-integration/Open-ILS/web/js/ui/default/vandelay/vandelay.js
   branches/serials-integration/Open-ILS/web/opac/common/js/opac_utils.js
   branches/serials-integration/Open-ILS/web/opac/locale/en-US/conify.dtd
   branches/serials-integration/Open-ILS/web/opac/locale/en-US/lang.dtd
   branches/serials-integration/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_summary.xml
   branches/serials-integration/Open-ILS/web/templates/base.tt2
   branches/serials-integration/Open-ILS/web/templates/default/acq/po/view.tt2
   branches/serials-integration/Open-ILS/web/templates/default/actor/user/register_table.tt2
   branches/serials-integration/Open-ILS/web/templates/default/circ/selfcheck/audio_config.tt2
   branches/serials-integration/Open-ILS/web/templates/default/conify/global/action_trigger/event_definition.tt2
   branches/serials-integration/Open-ILS/web/templates/default/conify/global/config/hold_matrix_matchpoint.tt2
   branches/serials-integration/Open-ILS/web/templates/default/vandelay/inc/item_attrs.tt2
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/auth/session.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/cat/opac.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/circ/offline.xul
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/constants.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.xul
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame.xul
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/deck.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/list.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/network.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/print.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/rbrowser.xul
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/widgets.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/window.js
   branches/serials-integration/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties
   branches/serials-integration/Open-ILS/xul/staff_client/external/dojo_template.xul
   branches/serials-integration/Open-ILS/xul/staff_client/external/template.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/admin/do_not_auto_attempt_print_setting.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/admin/font_settings.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/admin/index.xhtml
   branches/serials-integration/Open-ILS/xul/staff_client/server/admin/offline_manage_xacts.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/admin/transit_list.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/admin/work_log.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_buckets_overlay.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_editor.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_notes.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_new.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_view.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets_overlay.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/util.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_buckets.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_brief.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_summary.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/copy_details.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/missing_pieces.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/pre_cat_fields.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/print_list_template_editor.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/circ/util.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties
   branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties
   branches/serials-integration/Open-ILS/xul/staff_client/server/main/data.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/main/simple_auth.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/main/verify_credentials.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/barcode_entry.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill2.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_cc_info.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_check_info.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_details.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_wizard.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_overlay.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/edit_standing_penalty.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_cancel.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_notes.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_stat_cats.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_surveys.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items_overlay.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/new_standing_penalty.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form_horiz.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_result.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/standing_penalties.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/summary.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue_config.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/patron/user_buckets.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/notes.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sdist_editor.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/siss_editor.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sitem_editor.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/serial/ssub_editor.xul
   branches/serials-integration/Open-ILS/xul/staff_client/server/skin/patron_display.css
   branches/serials-integration/build/tools/update.sh
   branches/serials-integration/build/tools/update_db.sh
Log:
Catch up to trunk phase 2 (the rest) -- Merged revisions 17889,17891-17892,17894,17896-17897,17899,17901,17903,17905-17906,17908,17910,17912-17915,17917-17918,17922,17927,17929,17931,17933,17935,17937-17939,17941,17944-17946,17948,17950,17952,17954,17956-17958,17963,17965,17972,17974,17976,17978,17980,17982,17984-17985,17987,17989,17991-17992,17994-17995,17997,17999,18001-18007,18009,18011,18013,18015-18017,18021,18023,18025,18027,18029,18034,18037,18039,18041,18043,18045,18047,18049,18051,18054-18058,18062,18068,18072-18073,18075-18077,18079,18081,18083-18085,18089,18091,18093,18095,18097,18099,18101,18103,18105-18106,18109,18111-18113,18116,18121,18123-18124,18126,18128,18130,18132-18133,18136,18138,18140-18141,18146,18148,18150-18151,18153,18158-18159,18163,18165,18167,18169,18171-18172,18175,18177,18179-18180,18182-18185,18187-18188,18191-18193,18195-18196,18198-18199,18202-18203,18205,18207,18210-18211,18213,18215,18217,18219-18220,18222,18224,18226,18228,18230-18231,18233,18239-18240,18244-18245,18247,18249,18251,18253,18255-18256,18258-18262,18264,18269,18271-18272,18274,18277-18282,18285-18291,18293-18294,18298-18300,18303,18310-18313,18321-18323,18326,18329,18331,18333,18336,18342-18343,18349,18351,18353,18364-18366 via svnmerge from 
svn://svn.open-ils.org/ILS/trunk

........
  r17889 | scottmk | 2010-09-22 11:59:06 -0400 (Wed, 22 Sep 2010) | 8 lines
  
  For action.circulation and action.aged_circulation: add indexes
  on the target_copy column.
  
  This change was made in upgrade script # 0017, but was apparently
  never applied to the base install script.
  
  M    Open-ILS/src/sql/Pg/090.schema.action.sql
........
  r17891 | scottmk | 2010-09-22 15:16:26 -0400 (Wed, 22 Sep 2010) | 7 lines
  
  Adding a trigger.  Upgrade # 0364 created the trigger function but not
  the trigger itself.  However the base install script 040.schema.asset.sql
  creates both the function and the trigger.
  
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0414.schema.call-number-upd-ins-trigger.sql
........
  r17892 | phasefx | 2010-09-22 15:31:07 -0400 (Wed, 22 Sep 2010) | 1 line
  
  Holdings Maintenance used to make too many assumptions about what a decent org hierarchy looked like, and had these rules to suppress some of the fetch-render-happy behavior that existed when clicking on org units.  We can remove them now, and this allows volumes to render when owned by the top of the org tree
........
  r17894 | phasefx | 2010-09-22 15:37:43 -0400 (Wed, 22 Sep 2010) | 2 lines
  
  fixes a bug in Item Status -> Alternate View when the item being viewed has no circ modifier
........
  r17896 | phasefx | 2010-09-22 16:01:55 -0400 (Wed, 22 Sep 2010) | 1 line
  
  fix from tsbere to handle case where the print strategy is set to custom/external but the actual command is missing (perhaps due to xulrunner upgrades?)
........
  r17897 | phasefx | 2010-09-22 16:01:59 -0400 (Wed, 22 Sep 2010) | 1 line
  
  set label printer context for label printing
........
  r17899 | senator | 2010-09-22 20:14:03 -0400 (Wed, 22 Sep 2010) | 3 lines
  
  Hopefully fix a holds list sorting issue that only manifested when printing.
........
  r17901 | senator | 2010-09-22 20:34:22 -0400 (Wed, 22 Sep 2010) | 2 lines
  
  Another IDL chunking fix for web/templates-based interfaces
........
  r17903 | miker | 2010-09-22 22:12:02 -0400 (Wed, 22 Sep 2010) | 1 line
  
  only flesh up to, but not including, the leaf for the group_field
........
  r17905 | miker | 2010-09-22 22:49:36 -0400 (Wed, 22 Sep 2010) | 1 line
  
  Use a transaction to avoid talking to a replicated db when building A/T data structures
........
  r17906 | dbs | 2010-09-22 23:09:19 -0400 (Wed, 22 Sep 2010) | 7 lines
  
  Remove most UI annoyances from authority management interface
  
    * term input field now gets the focus automatically
    * pressing enter in most places submits a new search
    * removed the onBlur event as that required users to click a second
      time to open the action menu
........
  r17908 | dbs | 2010-09-22 23:18:14 -0400 (Wed, 22 Sep 2010) | 6 lines
  
  Browse through 20 authority records at a time in management interface
  
  The default browse list is set to 9 elements, but we have enough vertical
  space to make use of more. Perhaps we should check the font size and
  viewport height and then adjust accordingly, but that would be Hard.
........
  r17910 | dbs | 2010-09-22 23:45:16 -0400 (Wed, 22 Sep 2010) | 10 lines
  
  Remove most annoying UI "feature" of the new authority browse list interface
  
  When I added the new authority browse list interface, I used dojo.xhrGet()
  to retrieve records from the authority browse backend - but because I didn't
  specify sync:true, when you first right-clicked on a subfield, the function
  would return immediately and default to showing the context menu. You would
  then need to click two more times to show the authority list.
  
  Now you get it on your first right-click, as it should be.
........
  r17912 | miker | 2010-09-23 00:33:39 -0400 (Thu, 23 Sep 2010) | 1 line
  
  Have the CStoreEditor grow a DESTROY
........
  r17913 | erickson | 2010-09-23 01:31:03 -0400 (Thu, 23 Sep 2010) | 1 line
  
  event firing util code needs to run in a xact
........
  r17914 | erickson | 2010-09-23 03:17:00 -0400 (Thu, 23 Sep 2010) | 1 line
  
  fetch last updated event before comitting to stay in the xact
........
  r17915 | scottmk | 2010-09-23 09:45:06 -0400 (Thu, 23 Sep 2010) | 8 lines
  
  Dropping and recreating a foreign key constraint for config.metabib_field,
  in order to change its name.  WHen this foreign key was first introduced,
  the upgrade script gave it one name and the base install script gave it
  a different name.  Here we bring the names into sync.
  
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0415.schema.rename-field-class-fkey.sql
........
  r17917 | scottmk | 2010-09-23 10:31:32 -0400 (Thu, 23 Sep 2010) | 5 lines
  
  Rename a couple of indexes (recuring -> recurring)
  
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0416.schema.rename-recuring-idx.sql
........
  r17918 | scottmk | 2010-09-23 11:22:29 -0400 (Thu, 23 Sep 2010) | 12 lines
  
  Drop the never-used column item_count from acq.lineitem.
  Drop it also from the associated history table, and rebuild
  the function that maintains it.  Finally, rebuild the
  associated lifecycle view.
  
  Apply to trunk only; this column never existed in 2.0.
  The column has already been removed from the base installation
  script.
  
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0417.schema.acq.drop-lineitem-item-count.sql
........
  r17922 | miker | 2010-09-23 11:38:46 -0400 (Thu, 23 Sep 2010) | 11 lines
  
  Forward-port of a patch from Steve Callendar, via James Fournie, via launchpad:
  
  When the patron.password.use_phone is set, new patrons are created with their password set to the last 4 digits of their phone number, HOWEVER, when a patron's password is reset, it does not work properly. Although the little underlined summary shows the proper 4 digits, the password box displays 9-ish digits, and is not the last 4 digits of the password.
  
  The attached patch was created by Steve Callender and is confirmed working on 1.6.0
  
  ORIGINAL CAVEAT: This patch will not work on 2.0 as that has a new user editor, but it would presumably be worthwhile to verify this functionality works in that editor as well.
  
  ED NOTE: Because it is possible to use the old editor -- it still exists -- this patch should be applied.  It does what it advertises to do, which is fix the old editor to follow the "use phone number" OU setting.
........
  r17927 | phasefx | 2010-09-23 12:35:20 -0400 (Thu, 23 Sep 2010) | 2 lines
  
  disable holds Detail View button until the asynchronously loading details UI is ready
........
  r17929 | scottmk | 2010-09-23 14:58:36 -0400 (Thu, 23 Sep 2010) | 5 lines
  
  esolving various discrepancies between a freshly built 2.0
  database and an upgraded one.
  
  M    Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
........
  r17931 | erickson | 2010-09-23 16:47:47 -0400 (Thu, 23 Sep 2010) | 1 line
  
  repaired search call for user_setting. cstoreeditor uses the fieldmapper name, sos/usr/user/
........
  r17933 | senator | 2010-09-23 16:59:13 -0400 (Thu, 23 Sep 2010) | 2 lines
  
  IE hates the dangling comma (sometimes).
........
  r17935 | erickson | 2010-09-23 19:01:06 -0400 (Thu, 23 Sep 2010) | 1 line
  
  I can has authoritative for selfcheck receipts
........
  r17937 | miker | 2010-09-23 19:38:10 -0400 (Thu, 23 Sep 2010) | 1 line
  
  Stopping the leak
........
  r17938 | miker | 2010-09-23 19:47:49 -0400 (Thu, 23 Sep 2010) | 1 line
  
  reverting previous. sorry, folks
........
  r17939 | miker | 2010-09-23 19:49:26 -0400 (Thu, 23 Sep 2010) | 1 line
  
  Stopping the leak (let us try this again)
........
  r17941 | senator | 2010-09-23 20:15:31 -0400 (Thu, 23 Sep 2010) | 2 lines
  
  Re-commit miker's PermaCrud changes, just a tad more nicer ;-)
........
  r17944 | scottmk | 2010-09-23 20:56:21 -0400 (Thu, 23 Sep 2010) | 10 lines
  
  Apply some fixes that were earlier applied to the base installation script
  090.schema.action.sql, but not in an upgrade script.
  
  Also: correct a typo (INTEVAL -> INTERVAL).  Hence this upgrade needs to go
  to v2.0 as well as to trunk.
  
  M    Open-ILS/src/sql/Pg/090.schema.action.sql
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0418.function.action.fix-purge-circ.sql
........
  r17945 | miker | 2010-09-23 21:46:41 -0400 (Thu, 23 Sep 2010) | 1 line
  
  correct the comment
........
  r17946 | miker | 2010-09-23 21:47:06 -0400 (Thu, 23 Sep 2010) | 1 line
  
  use _session_request in RO calls
........
  r17948 | phasefx | 2010-09-23 22:36:55 -0400 (Thu, 23 Sep 2010) | 2 lines
  
  We're starting to get events where ilsevent == empty string now, not just null or a number.
........
  r17950 | senator | 2010-09-24 00:45:42 -0400 (Fri, 24 Sep 2010) | 3 lines
  
  Be more tolerant of long-running A/T event handling for holds pull list printing
........
  r17952 | phasefx | 2010-09-24 01:48:39 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  this call returns an array; fixes surprise stat cats in patron display
........
  r17954 | gmc | 2010-09-24 12:37:23 -0400 (Fri, 24 Sep 2010) | 9 lines
  
  improve hold targetting
  
  * all potential capturable copies are now checked (up to the first
    one that is permitted for the request), instead of a small random
    subset of them
  * don't do redundant permission checks
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r17956 | miker | 2010-09-24 13:45:49 -0400 (Fri, 24 Sep 2010) | 1 line
  
  excise the no-potentials check, should only effect frozen holds
........
  r17957 | miker | 2010-09-24 13:56:31 -0400 (Fri, 24 Sep 2010) | 1 line
  
  put the frozen (and also empty issuance or last-copy-removed) check back in as a conditional select if we found nothing mapped
........
  r17958 | phasefx | 2010-09-24 14:04:59 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  don't styled the juvenile indicator in the patron display based on age calculations by default, just go by the juvenile flag on the user.  local CSS can restore the behavior if desired
........
  r17963 | miker | 2010-09-24 14:08:05 -0400 (Fri, 24 Sep 2010) | 1 line
  
  cut-n-paste-o ... removing bad "my"
........
  r17965 | phasefx | 2010-09-24 14:23:10 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  spawn item attribute editor instead of item notes UI during process missing pieces workflow
........
  r17972 | erickson | 2010-09-24 15:30:10 -0400 (Fri, 24 Sep 2010) | 1 line
  
  removed extra {}'s from where clause
........
  r17974 | senator | 2010-09-24 16:11:12 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  Fix an autogrid create dialog of the kind that gets big, unusable and jumpy
........
  r17976 | miker | 2010-09-24 16:29:43 -0400 (Fri, 24 Sep 2010) | 1 line
  
  Remove confusing older targeter version; move status trimming to earlier in the process
........
  r17978 | erickson | 2010-09-24 18:23:58 -0400 (Fri, 24 Sep 2010) | 1 line
  
  clean up the fire_object_events cstore xact
........
  r17980 | erickson | 2010-09-24 19:04:45 -0400 (Fri, 24 Sep 2010) | 1 line
  
  more transaction cleanups
........
  r17982 | senator | 2010-09-24 20:56:24 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  Maybe not abandon poor little cstore so much.
........
  r17984 | miker | 2010-09-24 20:58:35 -0400 (Fri, 24 Sep 2010) | 1 line
  
  pedantic protection of cstore backends -- always use die_event when in xact mode, and rollback otherwise
........
  r17985 | senator | 2010-09-24 21:13:31 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  paranoia
........
  r17987 | erickson | 2010-09-24 23:27:20 -0400 (Fri, 24 Sep 2010) | 1 line
  
  events don't have granularity's.  removing
........
  r17989 | senator | 2010-09-24 23:45:21 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  How did this get lost? PermaCrud authoritative might work now?
........
  r17991 | erickson | 2010-09-24 23:46:03 -0400 (Fri, 24 Sep 2010) | 1 line
  
  no need to start a xact in the holds pull list.  really long lists can results in sending a rollback on a timed-out cstore handle, resulting in errors
........
  r17992 | senator | 2010-09-24 23:54:09 -0400 (Fri, 24 Sep 2010) | 2 lines
  
  fix misplaced paranoia
........
  r17994 | erickson | 2010-09-25 00:09:57 -0400 (Sat, 25 Sep 2010) | 1 line
  
  only need to wrap event retrieve in xact for latest copy.  otherwise, run the risk of xact-ed cstore timing out
........
  r17995 | gmc | 2010-09-25 22:42:50 -0400 (Sat, 25 Sep 2010) | 6 lines
  
  don't leak cstores if CStoreEditor rollback fails
  
  Patch by Mike Rylander.
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r17997 | gmc | 2010-09-25 22:49:39 -0400 (Sat, 25 Sep 2010) | 10 lines
  
  more selfcheck receipt transaction hackery
  
  Instead of having authoritative versions of 
  open-ils.circ.fire_*_trigger_events, wrap just the 
  target retrieval itself in a transaction.  Avoids as 
  yet unexplained rollback failure that occurs if
  processing a selfcheck receipt with more than a few items on it.
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r17999 | phasefx | 2010-09-25 23:17:05 -0400 (Sat, 25 Sep 2010) | 2 lines
  
  make address_type required in the patron editor, otherwise a blank value sends a null to the db where it silently fails
........
  r18001 | phasefx | 2010-09-26 02:34:22 -0400 (Sun, 26 Sep 2010) | 2 lines
  
  Show cover art in Z39.50 client.  I'm slow, so it only hit me this year that we can show added content for material not actually in the catalog yet
........
  r18002 | phasefx | 2010-09-26 03:05:58 -0400 (Sun, 26 Sep 2010) | 2 lines
  
  make the splitters in the z39.50 interface sticky
........
  r18003 | phasefx | 2010-09-26 04:06:33 -0400 (Sun, 26 Sep 2010) | 2 lines
  
  make all the splitters sticky with oils_persist
........
  r18004 | phasefx | 2010-09-26 05:23:43 -0400 (Sun, 26 Sep 2010) | 2 lines
  
  Call persist_helper() in most interfaces.  Give it the ability to handle resizing windows and set some windows up so that their height, width, and maximized state persist
........
  r18005 | phasefx | 2010-09-26 06:02:38 -0400 (Sun, 26 Sep 2010) | 2 lines
  
  make the MARC editor optional with Z39.50
........
  r18006 | phasefx | 2010-09-26 06:29:36 -0400 (Sun, 26 Sep 2010) | 2 lines
  
  unmark record for overlay after it is overlaid
........
  r18007 | miker | 2010-09-26 11:31:41 -0400 (Sun, 26 Sep 2010) | 1 line
  
  allow more complex facet ordering
........
  r18009 | miker | 2010-09-26 11:53:21 -0400 (Sun, 26 Sep 2010) | 1 line
  
  Only show facetOrder limited facets that have values
........
  r18011 | gmc | 2010-09-26 12:26:21 -0400 (Sun, 26 Sep 2010) | 4 lines
  
  added missing columns to CDBI table definitions
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18013 | dbs | 2010-09-26 14:34:47 -0400 (Sun, 26 Sep 2010) | 2 lines
  
  Remove a debugging statement that slipped in
........
  r18015 | erickson | 2010-09-26 19:20:10 -0400 (Sun, 26 Sep 2010) | 1 line
  
  push copy location order update into ML method.  return updated orders from method to prevent replication issues.  update UI JS to match
........
  r18016 | erickson | 2010-09-26 19:20:11 -0400 (Sun, 26 Sep 2010) | 1 line
  
  no need to warn when emails may just be disabled
........
  r18017 | erickson | 2010-09-26 19:20:12 -0400 (Sun, 26 Sep 2010) | 1 line
  
  honor SIP return date as the circ backdate
........
  r18021 | gmc | 2010-09-26 21:43:12 -0400 (Sun, 26 Sep 2010) | 4 lines
  
  avoid multiple clicks of selfcheck logout link
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18023 | scottmk | 2010-09-27 08:45:57 -0400 (Mon, 27 Sep 2010) | 5 lines
  
  Resolve differences between stored procedures in a freshly installed
  2.0 database and those in an upgraded one.
  
  M    Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
........
  r18025 | miker | 2010-09-27 11:23:10 -0400 (Mon, 27 Sep 2010) | 1 line
  
  Provide for limiting the number of classOrder elements to a specific number. Skips those without facet values.
........
  r18027 | miker | 2010-09-27 12:15:14 -0400 (Mon, 27 Sep 2010) | 1 line
  
  Adjust hold-to-record view to cover I, F and R hold types
........
  r18029 | phasefx | 2010-09-27 12:29:19 -0400 (Mon, 27 Sep 2010) | 2 lines
  
  since we're enforcing the requiredness of address type, let's give it a default value (since empty strings don't work with these widgets)
........
  r18034 | miker | 2010-09-27 14:26:29 -0400 (Mon, 27 Sep 2010) | 1 line
  
  use transaction when gathering records from a newly created queue; adjust rollback usage in import loop
........
  r18037 | gmc | 2010-09-27 14:50:56 -0400 (Mon, 27 Sep 2010) | 4 lines
  
  fix default path to selfcheck sounds
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18039 | miker | 2010-09-27 15:11:21 -0400 (Mon, 27 Sep 2010) | 1 line
  
  make pubdate sorting on search faster by pre-munging date1 and date2 into an acceptable sortkey
........
  r18041 | miker | 2010-09-27 15:16:57 -0400 (Mon, 27 Sep 2010) | 1 line
  
  Correct the subquery for A/T opt_in_setting
........
  r18043 | miker | 2010-09-27 15:24:13 -0400 (Mon, 27 Sep 2010) | 1 line
  
  missed in the previous commit, sorry folks
........
  r18045 | gmc | 2010-09-27 15:27:04 -0400 (Mon, 27 Sep 2010) | 7 lines
  
  fix glitch in hold target OU weighting
  
  We want to weight by the copy's circulation library, not the 
  pickup library.
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18047 | gmc | 2010-09-27 15:48:33 -0400 (Mon, 27 Sep 2010) | 4 lines
  
  fix another hold targeting glitch
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18049 | gmc | 2010-09-27 16:14:49 -0400 (Mon, 27 Sep 2010) | 7 lines
  
  adjustments to pubdate sorting patch
  
  * handle upgrade for date1/date2 containing the empty string
  * syntax error fix
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18051 | senator | 2010-09-27 16:54:56 -0400 (Mon, 27 Sep 2010) | 2 lines
  
  Special overriden widgets need forcible population when used in edit dialogs
........
  r18054 | scottmk | 2010-09-27 17:31:17 -0400 (Mon, 27 Sep 2010) | 12 lines
  
  1. Turn some ints into bigints.
  
  2. Rename the uniqueness constraint for booking.resource_type.
  
  M    Open-ILS/src/sql/Pg/090.schema.action.sql
  M    Open-ILS/src/sql/Pg/200.schema.acq.sql
  M    Open-ILS/src/sql/Pg/012.schema.vandelay.sql
  M    Open-ILS/src/sql/Pg/095.schema.booking.sql
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  M    Open-ILS/src/sql/Pg/070.schema.container.sql
  A    Open-ILS/src/sql/Pg/upgrade/0421.schema.embiggen-ints.sql
........
  r18055 | atz | 2010-09-27 21:22:40 -0400 (Mon, 27 Sep 2010) | 3 lines
  
  For edi_translator on RHEL
  
  Note: RubyGems 1.3.4 is not available anymore
........
  r18056 | atz | 2010-09-27 21:22:41 -0400 (Mon, 27 Sep 2010) | 1 line
  
  Fix POD for RemoteAccount
........
  r18057 | atz | 2010-09-27 21:24:20 -0400 (Mon, 27 Sep 2010) | 1 line
  
  Minor cleanup
........
  r18058 | atz | 2010-09-27 21:26:07 -0400 (Mon, 27 Sep 2010) | 1 line
  
  typo in POD
........
  r18062 | dbs | 2010-09-27 23:08:48 -0400 (Mon, 27 Sep 2010) | 5 lines
  
  Use quoted attribute values to make Firefox / XULRunner happier about dojo queries
  
  The unquoted values in BibTemplate and the OPAC detail template were generating
  mucho noise in the JavaScript console; this hushes it up nicely.
........
  r18068 | dbs | 2010-09-27 23:32:28 -0400 (Mon, 27 Sep 2010) | 11 lines
  
  Add a placeholder server/skin/custom.js to prevent one error in JS console
  
  custom.js enables you to override the settings in the stock constants.js
  to skin the behaviour of your staff client at your installation, without
  being subject to grief at upgrade time.
  
  Not having a custom.js file at all was, however, causing errors to be generated
  in the JS console as XULRunner tried hard to parse the 404 message as
  JavaScript. I suppose one could have changed the 404 message to valid
  JavaScript... naw, that would be evil.
........
  r18072 | scottmk | 2010-09-28 01:13:32 -0400 (Tue, 28 Sep 2010) | 6 lines
  
  Turn an int into a bigint in acq.acq_lineitem_history, following up on
  a similar change to acq.lineitem.
  
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0422.schema.acq.lineitem-history-bigint.sql
........
  r18073 | scottmk | 2010-09-28 01:17:03 -0400 (Tue, 28 Sep 2010) | 4 lines
  
  Incorporate several recent upgrade scripts, through # 0422.
  
  M    Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
........
  r18075 | dbs | 2010-09-28 02:18:07 -0400 (Tue, 28 Sep 2010) | 6 lines
  
  Silence SQL warnings from O:A:Storage:Publisher:metabib
  
  PostgreSQL wants strings containing regular expressions to be prefixed
  with E; this patch considerably reduces the noise in open-ils.storage_unix.log
  by complying with PostgreSQL's wishes.
........
  r18076 | dbs | 2010-09-28 02:43:41 -0400 (Tue, 28 Sep 2010) | 13 lines
  
  Make authority validation rules match authority ingest rules for better matches
  
  This resolves a problem in O:A:Storage:Publisher:authority:validate_tag() where
  the full NACO normalization rules that are applied to the subfields of the
  authority records when they are ingested into authority.full_rec are not similarly
  applied to the incoming subfields of the bib field that is being validated;
  only diacritic characters in the bib field subfields were being normalized.
  
  Now we apply naco_normalize() to the search terms so that they will match
  the ingested form of the authority record.
  
  Addresses https://bugs.launchpad.net/evergreen/+bug/649556
........
  r18077 | miker | 2010-09-28 12:35:24 -0400 (Tue, 28 Sep 2010) | 1 line
  
  add support for the null() xpath function, which works in pgxml (AKA xml2) but not in the builtin XPATH function for 8.3+
........
  r18079 | dbs | 2010-09-28 12:40:02 -0400 (Tue, 28 Sep 2010) | 7 lines
  
  Change memcached default location to 127.0.0.1 from localhost
  
  Debian Squeeze currently doesn't fare well with memcached servers pointing to 
  localhost and needs an explicit 127.0.0.1, whereas Ubuntu Lucid is happy with
  either. Changing the default here means one less possible gotcha in the out
  of the box install & configure experience for Debian Squeeze folk.
........
  r18081 | phasefx | 2010-09-28 12:46:34 -0400 (Tue, 28 Sep 2010) | 1 line
  
  this breaks (hides) the buttonbar in windows.  Sorry James, miker
........
  r18083 | erickson | 2010-09-28 14:12:12 -0400 (Tue, 28 Sep 2010) | 1 line
  
  repaired logic bug in lineitem worksheet template
........
  r18084 | erickson | 2010-09-28 14:12:13 -0400 (Tue, 28 Sep 2010) | 5 lines
  
  ability to override checkin events. minor fixes.
  
  repaired bug that caused checkin to sometimes fail when a hold
  was captured for a user that had at least 1 inactive card;
  made checkin override events configurable; minor cleanup
........
  r18085 | erickson | 2010-09-28 14:12:14 -0400 (Tue, 28 Sep 2010) | 1 line
  
  for troubleshooting, log the size of the template output to be stored
........
  r18089 | senator | 2010-09-28 16:26:09 -0400 (Tue, 28 Sep 2010) | 5 lines
  
  A/T: Send an early response back to the client when running all pending events
  The client may wish to know when the relativelty fast process of creating the
  events is over, and the relatively slow process of validating them and running
  their reactors/cleanup/etc is about to begin.
........
  r18091 | miker | 2010-09-28 16:45:17 -0400 (Tue, 28 Sep 2010) | 1 line
  
  move the early-out response so we avoid breaking the caller
........
  r18093 | phasefx | 2010-09-28 17:12:11 -0400 (Tue, 28 Sep 2010) | 2 lines
  
  Delete all cookies on logoff.  There's dojo code that looks for stale session cookies and has the xul client prompt for a new session.  When you logoff, the session is destroyed, but the stale cookies were being left behind (and not overwritten on login? I don't understand that part).  But this fixes it
........
  r18095 | miker | 2010-09-28 19:12:58 -0400 (Tue, 28 Sep 2010) | 1 line
  
  Use transactions everywhere in Vandelay
........
  r18097 | miker | 2010-09-29 10:45:50 -0400 (Wed, 29 Sep 2010) | 1 line
  
  Process item imports during record import, not as a secondary call (never should have worked, but for transaction timing)
........
  r18099 | miker | 2010-09-29 11:05:53 -0400 (Wed, 29 Sep 2010) | 1 line
  
  support per-grunlarity parallelizing via flag and lock file
........
  r18101 | gmc | 2010-09-29 13:48:52 -0400 (Wed, 29 Sep 2010) | 10 lines
  
  reporter: don't try to write Excel formulas
  
  Any cell value that starts with = is now always written
  as a text cell in spreadsheet output.  Avoids a possible
  exploit as well as errors like this:
  
  Couldn't parse formula: = at /openils/bin/clark-kent.pl line 429
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18103 | gmc | 2010-09-29 14:07:43 -0400 (Wed, 29 Sep 2010) | 4 lines
  
  fix FM type of reporter.report.data
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18105 | miker | 2010-09-29 16:16:41 -0400 (Wed, 29 Sep 2010) | 1 line
  
  Give BibTemplate the ability to inspect and optionally parse XML, instead of requiring a DOM node
........
  r18106 | phasefx | 2010-09-29 16:49:05 -0400 (Wed, 29 Sep 2010) | 2 lines
  
  Fix overzealous prompting for auth credentials, and some debug lines
........
  r18109 | erickson | 2010-09-30 16:49:14 -0400 (Thu, 30 Sep 2010) | 1 line
  
  fire the hold_request.cancel.staff when hold is cancelled by staff
........
  r18111 | atz | 2010-09-30 19:06:39 -0400 (Thu, 30 Sep 2010) | 6 lines
  
  Silence warning
  
  Warnings was:
  Use of uninitialized value in subroutine entry at /openils/lib/perl5/OpenILS/SIP/Patron.pm line 230.
  
  Signed-off-by: Joe Atzberger <atz at esilibrary.com>
........
  r18112 | dbs | 2010-10-01 01:16:41 -0400 (Fri, 01 Oct 2010) | 6 lines
  
  Add serial.record_entry to CDBI definitions
  
  Better late than never; never added the serial.record_entry
  CDBI definitions in the 1.6 series, but as that table can
  still be active in 2.0 we might as well get it in place.
........
  r18113 | dbs | 2010-10-01 01:24:38 -0400 (Fri, 01 Oct 2010) | 14 lines
  
  Teach marc_export script how to export MFHD serial records
  
  Passing the --mfhd flag will export any non-deleted MFHD records in
  serial.record_entry associated with each bib ID. So, for a hypothetical set of
  bib IDs 1, 2, 3, where 2 has no associated MFHD records and 3 has 2 MFHD records
  associated with it, the output will be structured as follows:
  
  Bib MARC for bib ID 1
  MFHD MARC for bib ID 1
  Bib MARC for bib ID 2
  Bib MARC for bib ID 3
  MFHD MARC for bib ID 3
  MFHD MARC for bib ID 2
........
  r18116 | phasefx | 2010-10-01 08:19:11 -0400 (Fri, 01 Oct 2010) | 2 lines
  
  tweak remoteauth.cgi to offer usrname and barcode params in addition to user.  user param now looks for the opac.barcode_regex org unit setting to determine whether the value is a usrname or barcode.  change double-quotes to single-quotes if we're not doing string interpolation.  change apache instructions for configuration
........
  r18121 | phasefx | 2010-10-01 14:07:00 -0400 (Fri, 01 Oct 2010) | 2 lines
  
  use an opac login here, which doesn't require the STAFF_LOGIN permission by default
........
  r18123 | miker | 2010-10-01 15:39:51 -0400 (Fri, 01 Oct 2010) | 1 line
  
  Implement a process-local cache for event target fleshing -- particularly helpful with large event groups
........
  r18124 | senator | 2010-10-01 15:45:27 -0400 (Fri, 01 Oct 2010) | 5 lines
  
  Offer yet another pull list printing pathway
  
  If you have pull lists long enough to make A/T groan, perhaps this will work
  better for you.
........
  r18126 | dbs | 2010-10-01 16:07:59 -0400 (Fri, 01 Oct 2010) | 11 lines
  
  Set due times for durations measured in days to 23:59:59 after inserts OR updates
  
  The existing trigger acted only on the initial insert of a circulation
  transaction for duration intervals perfectly divisible by 24 hours.
  If updates to those due dates were subsequently issued, then the due
  time would revert to 00:00:00 - which could cause surprising overdue
  fines to be generated on the due date, rather than after the due date.
  
  This commit makes the trigger take effect on both INSERT and UPDATE
  to the action.circulation table.
........
  r18128 | dbs | 2010-10-01 16:13:10 -0400 (Fri, 01 Oct 2010) | 2 lines
  
  Bring the 1.6.1-2.0 upgrade script up to date for the push_due_date_tgr
........
  r18130 | senator | 2010-10-01 17:48:41 -0400 (Fri, 01 Oct 2010) | 4 lines
  
  Fix an apparent bug in a case where OpenILS::WWW::Proxy means to send a
  redirect, and also avoid the issue altogether in the new holds pull list
  printing interface just added earlier today.
........
  r18132 | miker | 2010-10-02 01:47:02 -0400 (Sat, 02 Oct 2010) | 7 lines
  
  Massive search core-query speed improvement.
  
   * Only compile the tsquery once
   * Use direct ids instead of going back to the db udring a queyr
   * Change a remaining CASE to COALESCE/NULLIF
........
  r18133 | miker | 2010-10-02 02:04:05 -0400 (Sat, 02 Oct 2010) | 1 line
  
  putting back the NUMERIC cast, it is needed
........
  r18136 | miker | 2010-10-02 11:47:28 -0400 (Sat, 02 Oct 2010) | 1 line
  
  configurable chunking of the holds stream to avoid the xulrunner "I forgot the chunked stream" problem
........
  r18138 | scottmk | 2010-10-03 10:17:37 -0400 (Sun, 03 Oct 2010) | 4 lines
  
  Incorporate upgrade scripts 0423 and 0424
  
  M    Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
........
  r18140 | erickson | 2010-10-03 11:06:45 -0400 (Sun, 03 Oct 2010) | 1 line
  
  moved the cache clear to later in the firing to pick up data; fixed some typos/thinkos
........
  r18141 | miker | 2010-10-03 20:40:44 -0400 (Sun, 03 Oct 2010) | 1 line
  
  Allow caller to ignore selected facet classes; ignore identifier class by default
........
  r18146 | scottmk | 2010-10-04 09:43:13 -0400 (Mon, 04 Oct 2010) | 8 lines
  
  Add hold_priority column to permission.grp_tree table.
  
  M    Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/permission.pm
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0425.schema.perm-grp-tree-hold-priority.sql
  M    Open-ILS/src/sql/Pg/006.schema.permissions.sql
  M    Open-ILS/examples/fm_IDL.xml
........
  r18148 | senator | 2010-10-04 10:01:28 -0400 (Mon, 04 Oct 2010) | 2 lines
  
  Fix a slight bug in 18136, and the alt pull list printing interface works again.
........
  r18150 | senator | 2010-10-04 11:40:21 -0400 (Mon, 04 Oct 2010) | 2 lines
  
  Add a control for the hold_priority field in the permission group UI
........
  r18151 | miker | 2010-10-04 12:15:41 -0400 (Mon, 04 Oct 2010) | 1 line
  
  add support for hold_priority sorting in open-ils.storage.action.hold_request.nearest_hold
........
  r18153 | erickson | 2010-10-04 12:48:01 -0400 (Mon, 04 Oct 2010) | 1 line
  
  fixed typo in vandelay merge profile permission check
........
  r18158 | senator | 2010-10-04 14:57:20 -0400 (Mon, 04 Oct 2010) | 2 lines
  
  Add "Patron Alias" as an available column in pull list, holds shelf interfaces
........
  r18159 | dbs | 2010-10-04 15:05:59 -0400 (Mon, 04 Oct 2010) | 2 lines
  
  French translation of password reset form and prompts at /opac/password/fr-CA/
........
  r18163 | senator | 2010-10-04 17:28:17 -0400 (Mon, 04 Oct 2010) | 3 lines
  
  Add some missing ACQ perms. Other perms may be missing, but I /know/ these
  are needed.
........
  r18165 | erickson | 2010-10-05 10:15:40 -0400 (Tue, 05 Oct 2010) | 1 line
  
  avoid running activated hook when in dry run mode
........
  r18167 | gmc | 2010-10-05 10:44:21 -0400 (Tue, 05 Oct 2010) | 4 lines
  
  make it easier to grep for bib search durations from the Pg log
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18169 | phasefx | 2010-10-05 11:16:42 -0400 (Tue, 05 Oct 2010) | 8 lines
  
  more classname hooks for local CSS to latch on to if needed
  
  For example, you may have a server/skin/global_custom.css containing:
  
      .hide_patron_credit { display: none } 
      .hide_patron_work{ display: none } 
      .hide_patron_goods { display: none } 
........
  r18171 | atz | 2010-10-05 12:14:46 -0400 (Tue, 05 Oct 2010) | 1 line
  
  Add title to PO view page.
........
  r18172 | atz | 2010-10-05 12:16:59 -0400 (Tue, 05 Oct 2010) | 1 line
  
  EDI job POD
........
  r18175 | senator | 2010-10-05 12:37:57 -0400 (Tue, 05 Oct 2010) | 3 lines
  
  This is a silly commit, as this template should be made configurable down the
  road, maybe by OU setting or something. Anyway, make the list more legible.
........
  r18177 | atz | 2010-10-05 13:08:56 -0400 (Tue, 05 Oct 2010) | 1 line
  
  The whole point of test mode, NOT actually committing actions.
........
  r18179 | gmc | 2010-10-05 14:54:05 -0400 (Tue, 05 Oct 2010) | 8 lines
  
  hold matrix selection: treat root OU as just another OU
  
  Treat the root OU as just another OU for the purpose of
  calculating OU proximity adjustments when selecting a
  matchpoint.
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18180 | phasefx | 2010-10-05 15:33:08 -0400 (Tue, 05 Oct 2010) | 2 lines
  
  be quiet with errors retrieving remote column settings
........
  r18182 | phasefx | 2010-10-05 16:12:58 -0400 (Tue, 05 Oct 2010) | 1 line
  
  patch from tsbere to use _blank for new windows instead of generated identifiers
........
  r18183 | phasefx | 2010-10-05 16:13:01 -0400 (Tue, 05 Oct 2010) | 2 lines
  
  patch from tsbere to improve tab behavior and to give command-line options for controlling tabs
  See https://bugs.launchpad.net/evergreen/+bug/625056
........
  r18184 | phasefx | 2010-10-05 16:13:03 -0400 (Tue, 05 Oct 2010) | 1 line
  
  bug fix for close tab regression
........
  r18185 | phasefx | 2010-10-05 16:13:06 -0400 (Tue, 05 Oct 2010) | 1 line
  
  some I18N and tweak call to getIntPref.  Is the 2nd parameter version an undocumented way of providing a default if the pref isn't found?
........
  r18187 | miker | 2010-10-05 22:31:55 -0400 (Tue, 05 Oct 2010) | 1 line
  
  Add a retargetting specific version of the hold-permit function which skips user tests
........
  r18188 | miker | 2010-10-05 22:35:17 -0400 (Tue, 05 Oct 2010) | 1 line
  
  usr_grp is entirely unused currently; hide it
........
  r18191 | miker | 2010-10-05 23:07:30 -0400 (Tue, 05 Oct 2010) | 1 line
  
  Add a switch to turn on strict OU matching for all OUs required by a hold matchpoint
........
  r18192 | scottmk | 2010-10-06 09:27:26 -0400 (Wed, 06 Oct 2010) | 5 lines
  
  Fixed the second and third function definitions, which wouldn't compile.
  An SQL function cannot reference its parameters by name, but only by number.
  
  M    Open-ILS/src/sql/Pg/upgrade/0428.schema.hold_retarget.sql
........
  r18193 | erickson | 2010-10-06 09:28:09 -0400 (Wed, 06 Oct 2010) | 1 line
  
  first level of groups hash can be array or hash, leading to breakage;  for now, keep it simple and return continue-status to keep the caller alive
........
  r18195 | miker | 2010-10-06 09:43:52 -0400 (Wed, 06 Oct 2010) | 1 line
  
  Make retargetting check less lax by including appropriate patron-side test
........
  r18196 | miker | 2010-10-06 09:45:00 -0400 (Wed, 06 Oct 2010) | 1 line
  
  Start out exact-ou-match rules with an "always win" weight
........
  r18198 | miker | 2010-10-06 09:53:18 -0400 (Wed, 06 Oct 2010) | 1 line
  
  Thinko fix for field name. The script would not commit, so please simply reapply 0430.
........
  r18199 | erickson | 2010-10-06 09:54:26 -0400 (Wed, 06 Oct 2010) | 1 line
  
  suppress usr_grp field in hold matrix matchpoint;  see also 18188
........
  r18202 | scottmk | 2010-10-06 10:32:01 -0400 (Wed, 06 Oct 2010) | 5 lines
  
  Further propagating a syntax correction; SQL functions cannot reference
  their parameters by name.
  
  M    Open-ILS/src/sql/Pg/110.hold_matrix.sql
........
  r18203 | phasefx | 2010-10-06 10:59:43 -0400 (Wed, 06 Oct 2010) | 2 lines
  
  hold/transit slips may want to use stat cats as well
........
  r18205 | erickson | 2010-10-06 11:01:38 -0400 (Wed, 06 Oct 2010) | 1 line
  
  avoid retreiving/searching a linked object, when there is no object to retrieve (i.e. ident value is null) and silence various warnings by skipping this scenario.  mild variable refactor for easy reading for feeble eyes.
........
  r18207 | scottmk | 2010-10-06 11:55:14 -0400 (Wed, 06 Oct 2010) | 14 lines
  
  Two changes to config schema:
  
  1. Add new column date_ceiling to rule_circ_duration table.
  
  2. New table hard_due_date.
  
  For trunk, v1.6, v1.6.2 (eventually), and v2.1 (eventually).
  
  NOT for v1.6.1 or v2.0.
  
  M    Open-ILS/src/sql/Pg/002.schema.config.sql
  A    Open-ILS/src/sql/Pg/upgrade/0432.schema.config_hard_due_date.sql
  M    Open-ILS/examples/fm_IDL.xml
........
  r18210 | scottmk | 2010-10-06 14:19:43 -0400 (Wed, 06 Oct 2010) | 4 lines
  
  Incorporate upgrades 0427 and 0428 into the consolidated upgrade script.
  
  M    Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
........
  r18211 | miker | 2010-10-06 16:07:39 -0400 (Wed, 06 Oct 2010) | 1 line
  
  db upgrade script dislikes holes -- spackle this one
........
  r18213 | erickson | 2010-10-06 16:44:46 -0400 (Wed, 06 Oct 2010) | 1 line
  
  added support for honoring the due date ceiling from the duration rule.  AKA, end-of-semester due dates
........
  r18215 | senator | 2010-10-06 17:01:42 -0400 (Wed, 06 Oct 2010) | 14 lines
  
  New way to printing shelf-expired holds
  
  This just takes the newest template for printing hold pull lists and
  grafts this new functionality onto it.  It should perhaps be adjusted to also
  be able to print things on the holds shelf that /aren't/ shelf-expired.
  
  For now you get to this under Admin -> For Developers -> Local Administration
  
  This also corrects a bug because of which a "print pull list (alternate
  strategy)" button appeared where it shouldn't.
  
  This also removes the booking links from Admin -> For Developers -> Local
  Administration, as there are regular staff client menu entries for those now.
........
  r18217 | atz | 2010-10-06 18:12:36 -0400 (Wed, 06 Oct 2010) | 1 line
  
  Warn but continue on skipped upgrade revs.
........
  r18219 | erickson | 2010-10-07 10:56:43 -0400 (Thu, 07 Oct 2010) | 6 lines
  
  Parallel action/trigger collection and reaction
  
  QA'ed patch from miker to support parallel a/t event collection and
  reaction.  Max parallel procs is controlled by two new opensrf.xml
  trigger app_settings.  Sample config included, settings disabled by
  default.
........
  r18220 | phasefx | 2010-10-07 11:14:50 -0400 (Thu, 07 Oct 2010) | 2 lines
  
  destination_shelf macro for hold and hold/transit slips.  Will contain either HOLD SHELF, PUBLIC HOLD SHELF, or PRIVATE HOLD SHELF (these are localizable).  The latter two depend on the org unit setting 'circ.holds.behind_desk_pickup_supported' being in effect, and also considers a user setting for the holds user at the time of printing.
........
  r18222 | senator | 2010-10-07 13:18:58 -0400 (Thu, 07 Oct 2010) | 2 lines
  
  Add some reasonable default sorting to this expired holds list
........
  r18224 | atz | 2010-10-07 14:48:45 -0400 (Thu, 07 Oct 2010) | 6 lines
  
  EDI template update for ORDERS
  
  This template produces JSON for the edi translator to convert into
  actual EDI lines.  It now handles vendor-specific requirements for
  account and sub-account identification, and also transmits notes
  of the vendor-public variety as FTX segments in the lineitem.
........
  r18226 | senator | 2010-10-07 15:10:58 -0400 (Thu, 07 Oct 2010) | 3 lines
  
  Just a minor thing on this shelf expired print interface,
  probably more changes coming.
........
  r18228 | gmc | 2010-10-07 17:39:05 -0400 (Thu, 07 Oct 2010) | 4 lines
  
  choose the hold permit test variant correctly
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18230 | senator | 2010-10-07 18:37:45 -0400 (Thu, 07 Oct 2010) | 8 lines
  
  Expired holds shelf printer needs to be a holds shelf *clearer* and printer
  
  This needs cleaned up and stuff, and made into something cooler.
  Basically just does what XUL interfaces under the Circ menu can already do,
  but streamlined to tolerate really big datasets.
  
  Much of this code originates from berick and miker.
........
  r18231 | senator | 2010-10-07 18:46:24 -0400 (Thu, 07 Oct 2010) | 2 lines
  
  progress to ProgressDialog means total, not incremental
........
  r18233 | gmc | 2010-10-07 21:34:08 -0400 (Thu, 07 Oct 2010) | 8 lines
  
  do not apply superpage limit inside bib search joins
  
  Oddly, empirical evidence suggests that this might actually 
  be faster, and if this pans out, search results will definitely
  be more complete.
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18239 | dbs | 2010-10-07 23:30:00 -0400 (Thu, 07 Oct 2010) | 7 lines
  
  Avoid scary SSL / HTTPS errors in Apache configuration
  
  When port 443 is the last listener port, Apache generates lots
  of "unknown protocol speaking not SSL to HTTPS port!?" errors in
  the logs - which are scary, but harmless. Putting port 80 last
  avoids those errors entirely, per http://wiki.apache.org/httpd/InternalDummyConnection
........
  r18240 | scottmk | 2010-10-08 10:16:19 -0400 (Fri, 08 Oct 2010) | 19 lines
  
  Tidied up buildSELECT() a bit:
  
  1. Sprinkled the const qualifier here and there.
  
  2. Moved some variable declarations to get them closer to the point of
  first use, and to limit their scope.
  
  3. Renamed some variables to better reflect their meaning.
  
  4. Split a couple of variables into multiple variables, instead of using
  them for multiple unrelated purposes.
  
  5. Plugged a memory leak in the case of an error return.
  
  6. Added comments, including a Doxygen-style comment at the top of the
  function.
  
  M    Open-ILS/src/c-apps/oils_sql.c
........
  r18244 | miker | 2010-10-08 13:06:52 -0400 (Fri, 08 Oct 2010) | 1 line
  
  add a method to overlay a special bib container full of records (or bucket + template)
........
  r18245 | phasefx | 2010-10-08 13:29:20 -0400 (Fri, 08 Oct 2010) | 2 lines
  
  moving users in and out of groups produced dialogs that expected vertical patron summaries.  this fixes that
........
  r18247 | phasefx | 2010-10-08 13:38:24 -0400 (Fri, 08 Oct 2010) | 2 lines
  
  fix logic error where we were just testing for the presence of a user setting instead of its value
........
  r18249 | senator | 2010-10-08 14:02:08 -0400 (Fri, 08 Oct 2010) | 2 lines
  
  Be a little more tolerant of aberrant data when clearing the holds shelf
........
  r18251 | phasefx | 2010-10-08 16:58:37 -0400 (Fri, 08 Oct 2010) | 1 line
  
  typo
........
  r18253 | erickson | 2010-10-08 17:10:43 -0400 (Fri, 08 Oct 2010) | 1 line
  
  mild fixes for a/t interface admin interface.  sort by def name instead of hook, which probably makes more sense to a human.  hide the opt-in and max-delay columns to free up some badly needed horizontal space.  use percentage width for name column, which acts a lot like 'auto', but allows the user to manually resize
........
  r18255 | miker | 2010-10-08 22:32:49 -0400 (Fri, 08 Oct 2010) | 1 line
  
  mod_perl handler to allow batch update bib records from an ephemeral template
........
  r18256 | erickson | 2010-10-11 10:01:45 -0400 (Mon, 11 Oct 2010) | 1 line
  
  wait to run-pending if a specific granularity used
........
  r18258 | scottmk | 2010-10-11 10:25:05 -0400 (Mon, 11 Oct 2010) | 12 lines
  
  Pull out into a separate function: the code in SELECT() that builds a
  comma-separated list of ORDER BY expressions from a JSON_ARRAY.
  
  Invoke that function, not only from SELECT(), but also from the
  buildSELECT() function.
  
  As a result, the select methods will be able to use the same array
  syntax as json_query for ORDER BY clauses, as an alternative to the
  existing hash syntax.
  
  M    Open-ILS/src/c-apps/oils_sql.c
........
  r18259 | atz | 2010-10-11 11:21:12 -0400 (Mon, 11 Oct 2010) | 1 line
  
  Clean up method registration sigs/descs
........
  r18260 | atz | 2010-10-11 12:06:23 -0400 (Mon, 11 Oct 2010) | 1 line
  
  Method registration cleanup
........
  r18261 | miker | 2010-10-11 12:06:52 -0400 (Mon, 11 Oct 2010) | 1 line
  
  supply a dummy leader, as required by the Perl MARC modules
........
  r18262 | atz | 2010-10-11 12:07:09 -0400 (Mon, 11 Oct 2010) | 1 line
  
  Tighter verbose format for skipped POs
........
  r18264 | miker | 2010-10-11 12:07:47 -0400 (Mon, 11 Oct 2010) | 1 line
  
  Working template-based batch bib updater!
........
  r18269 | phasefx | 2010-10-11 16:39:31 -0400 (Mon, 11 Oct 2010) | 2 lines
  
  initialize these lists just once to prevent display glitch
........
  r18271 | miker | 2010-10-11 16:41:37 -0400 (Mon, 11 Oct 2010) | 1 line
  
  dogfooding cleanup
........
  r18272 | miker | 2010-10-11 16:42:46 -0400 (Mon, 11 Oct 2010) | 1 line
  
  make the source selection less confusing, and improve some wording in the template section (more to come)
........
  r18274 | phasefx | 2010-10-11 17:42:48 -0400 (Mon, 11 Oct 2010) | 2 lines
  
  avoid race condition with post-save patron editor refresh and replicated databases
........
  r18277 | scottmk | 2010-10-12 09:40:14 -0400 (Tue, 12 Oct 2010) | 4 lines
  
  Incorporate upgrade # 0433 into the consolidated upgrade script.
  
  M    Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
........
  r18278 | miker | 2010-10-12 11:19:54 -0400 (Tue, 12 Oct 2010) | 1 line
  
  skip unknown order_by entries instead of erroring
........
  r18279 | miker | 2010-10-12 11:39:47 -0400 (Tue, 12 Oct 2010) | 1 line
  
  rearrangement for variable existance
........
  r18280 | miker | 2010-10-12 11:40:45 -0400 (Tue, 12 Oct 2010) | 1 line
  
  and ... use a function name that exists
........
  r18281 | miker | 2010-10-12 12:02:31 -0400 (Tue, 12 Oct 2010) | 1 line
  
  now that we are working again, hush the warnings (osrfLogInternal to the rescue)
........
  r18282 | atz | 2010-10-12 13:01:52 -0400 (Tue, 12 Oct 2010) | 1 line
  
  Be sure to copy new JS files from OpenSRF on a FULL update/install
........
  r18285 | miker | 2010-10-12 15:23:22 -0400 (Tue, 12 Oct 2010) | 1 line
  
  improve labeling and documentation
........
  r18286 | phasefx | 2010-10-12 15:34:35 -0400 (Tue, 12 Oct 2010) | 1 line
  
  add option for hiding entire embedded browser toolbar
........
  r18287 | phasefx | 2010-10-12 15:34:39 -0400 (Tue, 12 Oct 2010) | 1 line
  
  allow util.deck to be instantiated with either a xul deck object or a xul deck id
........
  r18288 | phasefx | 2010-10-12 15:34:50 -0400 (Tue, 12 Oct 2010) | 1 line
  
  vertical bib summary (mainly for use in the Merge Record interface).  I tried using just bib_brief.xul with document.loadOverlay to choose between bib_brief_overlay_vertical.xul and bib_brief_overlay.xul based on a param, but was running into too much pain going that route.
........
  r18289 | phasefx | 2010-10-12 15:35:42 -0400 (Tue, 12 Oct 2010) | 2 lines
  
  new record merge UI.  needs r18287, r18288.  I'll backport them all together
........
  r18290 | miker | 2010-10-12 15:55:58 -0400 (Tue, 12 Oct 2010) | 1 line
  
  more terminology cleanup and inline documentation
........
  r18291 | miker | 2010-10-12 16:31:31 -0400 (Tue, 12 Oct 2010) | 1 line
  
  Get rid of commas, apparently confusing; make action button label more generic / less "developery"
........
  r18293 | miker | 2010-10-12 17:31:33 -0400 (Tue, 12 Oct 2010) | 1 line
  
  when we have no target field, add the entire source field, even when we have a subfield designation
........
  r18294 | phasefx | 2010-10-12 17:32:21 -0400 (Tue, 12 Oct 2010) | 1 line
  
  entry points for miker_'s batch marc editor
........
  r18298 | miker | 2010-10-12 20:27:46 -0400 (Tue, 12 Oct 2010) | 1 line
  
  deduplicate bibs going into the merge queue
........
  r18299 | miker | 2010-10-12 20:28:43 -0400 (Tue, 12 Oct 2010) | 1 line
  
  arg, missing semicolon
........
  r18300 | miker | 2010-10-12 20:29:38 -0400 (Tue, 12 Oct 2010) | 1 line
  
  double-arg, backwards logic
........
  r18303 | miker | 2010-10-12 22:33:59 -0400 (Tue, 12 Oct 2010) | 1 line
  
  thinko supporting multiple rules of the same type in in-line merge rulesets
........
  r18310 | phasefx | 2010-10-13 12:03:25 -0400 (Wed, 13 Oct 2010) | 2 lines
  
  missing semicolon
........
  r18311 | erickson | 2010-10-13 12:13:23 -0400 (Wed, 13 Oct 2010) | 1 line
  
  when the target for an event is no longer around, immediately invalidate the event and prevent the event from bubbling up for further processing
........
  r18312 | miker | 2010-10-13 12:17:14 -0400 (Wed, 13 Oct 2010) | 1 line
  
  Use just one transaction, and inside an rstore editor no less, for fleshing env paths
........
  r18313 | phasefx | 2010-10-13 12:30:38 -0400 (Wed, 13 Oct 2010) | 1 line
  
  overzealous trimming of cat.properties
........
  r18321 | scottmk | 2010-10-13 14:53:56 -0400 (Wed, 13 Oct 2010) | 22 lines
  
  Changes to the treatment of ORDER BY:
  
  1. For json_query: when ORDER BY is expressed as an object keyed on class
  (instead of an array of field specifications), and the class is not in
  scope, error out instead of silently ignoring the class.
  
  The other changes affect only methods other than json_query:
  
  2. When the ORDER BY list is provided as a raw text string: block any
  string containing a semicolon, in order to block simple SQL injections.
  For now we make no exceptions for quoted semicolons, which are not
  likely ever to appear an an ORDER BY clause.
  
  3. Keep virtual fields out of the ORDER BY clause.  For now we silently
  ignore them, as we ignore non-existent fields.  In both cases we should
  perhaps error out.
  
  4. Don't require that a class referenced in the ORDER BY clause also be
  referenced in the SELECT clause.  Just make sure it's in scope.
  
  M    Open-ILS/src/c-apps/oils_sql.c
........
  r18322 | erickson | 2010-10-13 16:16:57 -0400 (Wed, 13 Oct 2010) | 1 line
  
  clean up duplicate system-controlled penalties during penalty calculation; perform penalty trigger event creation after the standalone commit occurs.  todo, handle non-standalone post-commit penalty trigger event creation
........
  r18323 | erickson | 2010-10-13 16:16:58 -0400 (Wed, 13 Oct 2010) | 1 line
  
  default to standard cstore instead of rstore for env building editor
........
  r18326 | phasefx | 2010-10-14 11:29:29 -0400 (Thu, 14 Oct 2010) | 2 lines
  
  change the Swap Editor button to a persisted checkbox
........
  r18329 | miker | 2010-10-14 14:13:20 -0400 (Thu, 14 Oct 2010) | 1 line
  
  correct top-half ordering by label_sortkey
........
  r18331 | miker | 2010-10-14 14:35:27 -0400 (Thu, 14 Oct 2010) | 1 line
  
  patch from Steve Callendar to avoid resetting the passwd every time a phone number changes
........
  r18333 | miker | 2010-10-14 15:18:11 -0400 (Thu, 14 Oct 2010) | 1 line
  
  cast label_sortkey to bytea in order to get ascii-betical sorting in any locale, even C. stupid glibc ...
........
  r18336 | miker | 2010-10-14 15:33:41 -0400 (Thu, 14 Oct 2010) | 1 line
  
  backward compat indexing for label instead of label_sortkey
........
  r18342 | erickson | 2010-10-14 15:57:42 -0400 (Thu, 14 Oct 2010) | 1 line
  
  repaired upgrade version
........
  r18343 | miker | 2010-10-14 16:04:05 -0400 (Thu, 14 Oct 2010) | 1 line
  
  "as" not "to" ... you pointed that out the first time, miker, what is your deal?
........
  r18349 | miker | 2010-10-14 16:17:00 -0400 (Thu, 14 Oct 2010) | 1 line
  
  force granularity-only when any granularity is specified
........
  r18351 | erickson | 2010-10-14 18:17:21 -0400 (Thu, 14 Oct 2010) | 1 line
  
  If an item is captured for a hold, but not in transit (i.e. on holds shelf), set the destination_location equal to the pickup library (i.e where it's supposedly on the shelf).  This is useful for autmated sorting so that the item will return to the branch whose shelf where it belongs.
........
  r18353 | gmc | 2010-10-15 09:08:52 -0400 (Fri, 15 Oct 2010) | 7 lines
  
  tweak expanding search field aliases
  
  Avoids a glitch that can occur if a search field alias
  exists that happens to have the same name as a search field.
  
  Signed-off-by: Galen Charlton <gmc at esilibrary.com>
........
  r18364 | miker | 2010-10-16 11:32:32 -0400 (Sat, 16 Oct 2010) | 1 line
  
  use a function to wrap up escaping of solidus and casting to bytea, propogate to indexing and where/order_by
........
  r18365 | miker | 2010-10-16 11:35:39 -0400 (Sat, 16 Oct 2010) | 1 line
  
  need to update this index as well
........
  r18366 | miker | 2010-10-16 11:38:21 -0400 (Sat, 16 Oct 2010) | 1 line
  
  go ahead and use the new index if we need to
........



Property changes on: branches/serials-integration
___________________________________________________________________
Name: svnmerge-integrated
   - /trunk:1-17887
   + /trunk:1-18371

Modified: branches/serials-integration/Open-ILS/examples/apache/eg.conf
===================================================================
--- branches/serials-integration/Open-ILS/examples/apache/eg.conf	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/examples/apache/eg.conf	2010-10-18 14:05:06 UTC (rev 18372)
@@ -99,27 +99,7 @@
 ExpiresByType application/x-javascript A64800
 ExpiresByType text/css A3000
 
-
-
-
 # ----------------------------------------------------------------------------------
-# Set up our main virtual host
-# ----------------------------------------------------------------------------------
-NameVirtualHost *:80
-<VirtualHost *:80>
-	ServerName localhost:80
-	ServerAlias 127.0.0.1:80
- 	DocumentRoot /openils/var/web/
-	DirectoryIndex index.xml index.html index.xhtml
-    # - absorb the shared virtual host settings
-    Include eg_vhost.conf
-</VirtualHost>
-
-
-
-
-
-# ----------------------------------------------------------------------------------
 # Set up our SSL virtual host
 # ----------------------------------------------------------------------------------
 Listen 443
@@ -147,4 +127,18 @@
 
 </VirtualHost>
 
+# ----------------------------------------------------------------------------------
+# Set up our main virtual host
+# Port 80 comes after 443 to avoid "unknown protocol speaking not SSL to HTTPS port!?" 
+# errors, per http://wiki.apache.org/httpd/InternalDummyConnection
+# ----------------------------------------------------------------------------------
+NameVirtualHost *:80
+<VirtualHost *:80>
+	ServerName localhost:80
+	ServerAlias 127.0.0.1:80
+ 	DocumentRoot /openils/var/web/
+	DirectoryIndex index.xml index.html index.xhtml
+    # - absorb the shared virtual host settings
+    Include eg_vhost.conf
+</VirtualHost>
 

Modified: branches/serials-integration/Open-ILS/examples/apache/eg_vhost.conf
===================================================================
--- branches/serials-integration/Open-ILS/examples/apache/eg_vhost.conf	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/examples/apache/eg_vhost.conf	2010-10-18 14:05:06 UTC (rev 18372)
@@ -395,6 +395,28 @@
     allow from all
 </Location>
 
+<Location /opac/extras/merge_template>
+    SetHandler perl-script
+    PerlSetVar OILSProxyTitle "Batch Update Login"
+    PerlSetVar OILSProxyDescription "Please log in to update records in batch"
+    PerlSetVar OILSProxyPermissions "STAFF_LOGIN"
+    PerlHandler OpenILS::WWW::Proxy OpenILS::WWW::TemplateBatchBibUpdate
+    PerlSendHeader On
+    Options +ExecCGI
+    allow from all
+</Location>
+
+<Location /opac/extras/circ>
+    SetHandler perl-script
+    PerlSetVar OILSProxyTitle "Circ Extras Login"
+    PerlSetVar OILSProxyDescription "Please log in with an authorized staff account to export records"
+    PerlSetVar OILSProxyPermissions "STAFF_LOGIN"
+    PerlHandler OpenILS::WWW::Proxy
+    Options +ExecCGI
+    PerlSendHeader On
+    allow from all
+</Location>
+
 # ----------------------------------------------------------------------------------
 # Reporting output lives here
 # ----------------------------------------------------------------------------------

Modified: branches/serials-integration/Open-ILS/examples/fm_IDL.xml
===================================================================
--- branches/serials-integration/Open-ILS/examples/fm_IDL.xml	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/examples/fm_IDL.xml	2010-10-18 14:05:06 UTC (rev 18372)
@@ -999,6 +999,7 @@
 		<fields oils_persist:primary="id" oils_persist:sequence="config.hold_matrix_matchpoint_id_seq">
 			<field reporter:label="Matchpoint ID" name="id" reporter:datatype="id"/>
 			<field reporter:label="Active?" name="active" reporter:datatype="bool"/>
+			<field reporter:label="Strict OU matches?" name="strict_ou_match" reporter:datatype="bool"/>
 			<field reporter:label="User Home Library" name="user_home_ou" reporter:datatype="org_unit"/>
 			<field reporter:label="Request Library" name="request_ou" reporter:datatype="org_unit"/>
 			<field reporter:label="Pickup Library" name="pickup_ou" reporter:datatype="org_unit"/>
@@ -1868,6 +1869,7 @@
 			<field name="name" reporter:datatype="text"/>
 			<field name="normal" reporter:datatype="interval"/>
 			<field name="shrt" reporter:datatype="interval"/>
+			<field name="date_ceiling" reporter:datatype="timestamp"/>
 		</fields>
 		<links>
 		</links>
@@ -1880,6 +1882,21 @@
             </actions>
         </permacrud>
 	</class>
+
+	<class id="chdd" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::hard_due_date" oils_persist:tablename="config.hard_due_date" reporter:label="Hard Due Date">
+		<fields oils_persist:primary="id" oils_persist:sequence="config.hard_due_date_id_seq">
+			<field reporter:label="ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Duration Rule" name="duration_rule" reporter:datatype="link"/>
+			<field reporter:label="Ceiling Date" name="ceiling_date" reporter:datatype="timestamp"/>
+			<field reporter:label="Active Date" name="active_date" reporter:datatype="timestamp"/>
+		</fields>
+		<links>
+			<link field="duration_rule" reltype="has_a" key="id" map="" class="crcd"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+		</permacrud>
+	</class>
+
 	<class id="mobts" controller="open-ils.cstore" oils_obj:fieldmapper="money::open_billable_transaction_summary" oils_persist:tablename="money.open_billable_xact_summary" reporter:label="Open Billable Transaction Summary">
 		<fields oils_persist:primary="id" oils_persist:sequence="">
 			<field name="balance_owed" reporter:datatype="money"/>
@@ -4515,6 +4532,7 @@
 			<field reporter:label="User Expiration Interval" name="perm_interval" reporter:datatype="interval"/>
 			<field reporter:label="Required Permission" name="application_perm" reporter:datatype="text"/>
 			<field reporter:label="Is User Group" name="usergroup" reporter:datatype="bool"/>
+			<field reporter:label="Hold Priority" name="hold_priority" reporter:datatype="int"/>
 		</fields>
 		<links>
 			<link field="parent" reltype="has_a" key="id" map="" class="pgt"/>
@@ -6523,7 +6541,7 @@
 			<field name="owner" reporter:datatype="link"/>
 			<field name="create_time" reporter:datatype="timestamp"/>
 			<field name="template" reporter:datatype="link"/>
-			<field name="data" reporter:datatype="link"/>
+			<field name="data" reporter:datatype="text"/>
 			<field name="folder" reporter:datatype="link"/>
 			<field name="recur" reporter:datatype="bool"/>
 			<field name="recurrence" reporter:datatype="interval"/>

Modified: branches/serials-integration/Open-ILS/examples/oils_sip.xml.example
===================================================================
--- branches/serials-integration/Open-ILS/examples/oils_sip.xml.example	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/examples/oils_sip.xml.example	2010-10-18 14:05:06 UTC (rev 18372)
@@ -105,6 +105,15 @@
 					-->
 				</options>
 
+                <checkin_override>
+                    <event>COPY_ALERT_MESSAGE</event>
+                    <event>COPY_BAD_STATUS</event>
+                    <event>COPY_STATUS_MISSING</event>
+                    <!--
+                    <event>COPY_STATUS_LOST</event>
+                    -->
+                </checkin_override>
+
                 <!-- If uncommented, overrides the legacy_script_support value in opensrf.xml for SIP. -->
                 <!--
                 <legacy_script_support>false</legacy_script_support>

Modified: branches/serials-integration/Open-ILS/examples/opensrf.xml.example
===================================================================
--- branches/serials-integration/Open-ILS/examples/opensrf.xml.example	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/examples/opensrf.xml.example	2010-10-18 14:05:06 UTC (rev 18372)
@@ -326,14 +326,14 @@
             <!-- memcache servers -->
             <global>
                 <servers>
-                    <server>localhost:11211</server>
+                    <server>127.0.0.1:11211</server>
                 </servers>
                 <max_cache_time>86400</max_cache_time>
             </global>
             <anon>
                 <!-- anonymous cache.  currently, primarily used for web session caching -->
                 <servers>
-                    <server>localhost:11211</server>
+                    <server>127.0.0.1:11211</server>
                 </servers>
                 <max_cache_time>1800</max_cache_time>
                 <!-- maximum size of a single cache entry / default = 100k-->
@@ -590,6 +590,15 @@
                     <min_spare_children>1</min_spare_children>
                     <max_spare_children>5</max_spare_children>
                 </unix_config>
+                <app_settings>
+                    <!-- number of parallel open-ils.trigger processes to use for collection and reaction -->
+                    <!--
+                    <parallel>
+                        <collect>3</collect>
+                        <react>3</react>
+                    </parallel>
+                    -->
+                </app_settings>
             </open-ils.trigger>
 
             <opensrf.math>

Modified: branches/serials-integration/Open-ILS/examples/remoteauth.cgi
===================================================================
--- branches/serials-integration/Open-ILS/examples/remoteauth.cgi	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/examples/remoteauth.cgi	2010-10-18 14:05:06 UTC (rev 18372)
@@ -3,7 +3,8 @@
 #    This CGI script might be useful for providing an easy way for EZproxy to authenticate
 #    users against an Evergreen instance.
 #    
-#    For example, if you modify your eg_vhost.conf by adding this:
+#    For example, if you modify your eg.conf by adding this:
+#    Alias "/cgi-bin/ezproxy/" "/openils/var/cgi-bin/ezproxy/"
 #    <Directory "/openils/var/cgi-bin/ezproxy">
 #        AddHandler cgi-script .pl
 #        AllowOverride None
@@ -29,47 +30,63 @@
 use OpenSRF::EX qw(:try);
 use OpenSRF::System;
 
-
 my $bootstrap = '/openils/conf/opensrf_core.xml';
 my $cgi = new CGI;
 my $u = $cgi->param('user');
+my $usrname = $cgi->param('usrname');
+my $barcode = $cgi->param('barcode');
 my $p = $cgi->param('passwd');
 
 print $cgi->header(-type=>'text/html', -expires=>'-1d');
 
 OpenSRF::System->bootstrap_client( config_file => $bootstrap );
 
-if (!$u || !$p) {
-	print "+INCOMPLETE";
+if (!($u || $usrname || $barcode) || !$p) {
+	print '+INCOMPLETE';
 } else {
-	my $nametype = 'username';
-	$nametype = 'barcode' if ($u =~ /^\d+$/o);
+	my $nametype;
+    if ($usrname) {
+        $u = $usrname;
+	    $nametype = 'username';
+    } elsif ($barcode) {
+        $u = $barcode;
+        $nametype = 'barcode';
+    } else {
+	    $nametype = 'username';
+        my $regex_response = OpenSRF::AppSession
+            ->create('open-ils.actor')
+            ->request('open-ils.actor.ou_setting.ancestor_default', 1, 'opac.barcode_regex')
+            ->gather(1);
+        if ($regex_response) {
+            my $regexp = $regex_response->{'value'};
+            $nametype = 'barcode' if ($u =~ qr/$regexp/);
+        }
+    }
 	my $seed = OpenSRF::AppSession
-		->create("open-ils.auth")
+		->create('open-ils.auth')
 		->request( 'open-ils.auth.authenticate.init', $u )
 		->gather(1);
 	if ($seed) {
 		my $response = OpenSRF::AppSession
-			->create("open-ils.auth")
-			->request( 'open-ils.auth.authenticate.complete', { $nametype => $u, password => md5_hex($seed . md5_hex($p)), type => 'temp' })
+			->create('open-ils.auth')
+			->request( 'open-ils.auth.authenticate.complete', { $nametype => $u, password => md5_hex($seed . md5_hex($p)), type => 'opac' })
 			->gather(1);
 		if ($response->{payload}->{authtoken}) {
 			my $user = OpenSRF::AppSession
-				->create("open-ils.auth")
-				->request( "open-ils.auth.session.retrieve", $response->{payload}->{authtoken} )
+				->create('open-ils.auth')
+				->request( 'open-ils.auth.session.retrieve', $response->{payload}->{authtoken} )
 				->gather(1);
 			if (ref($user) eq 'HASH' && $user->{ilsevent} == 1001) {
-				print "+NO";
+				print '+NO';
 			} else {
-				print "+VALID";
+				print '+VALID';
 			}
 		} else {
-			print "+NO";
+			print '+NO';
 		}
 	} else {
-		print "+BACKEND_ERROR";
+		print '+BACKEND_ERROR';
 	}
-
 }
 
 1;

Modified: branches/serials-integration/Open-ILS/src/c-apps/oils_sql.c
===================================================================
--- branches/serials-integration/Open-ILS/src/c-apps/oils_sql.c	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/c-apps/oils_sql.c	2010-10-18 14:05:06 UTC (rev 18372)
@@ -90,8 +90,11 @@
 								 jsonObject*, const char*, osrfMethodContext* );
 static char* searchPredicate ( const ClassInfo*, osrfHash*, jsonObject*, osrfMethodContext* );
 static char* searchJOIN ( const jsonObject*, const ClassInfo* left_info );
-static char* searchWHERE ( const jsonObject*, const ClassInfo*, int, osrfMethodContext* );
-static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
+static char* searchWHERE ( const jsonObject* search_hash, const ClassInfo*, int, osrfMethodContext* );
+static char* buildSELECT( const jsonObject*, jsonObject* rest_of_query,
+	osrfHash* meta, osrfMethodContext* ctx );
+static char* buildOrderByFromArray( osrfMethodContext* ctx, const jsonObject* order_array );
+
 char* buildQuery( osrfMethodContext* ctx, jsonObject* query, int flags );
 
 char* SELECT ( osrfMethodContext*, jsonObject*, const jsonObject*, const jsonObject*,
@@ -4277,7 +4280,6 @@
 		jsonIteratorFree( selclass_itr );
 	}
 
-
 	char* col_list = buffer_release( select_buf );
 
 	// Make sure the SELECT list isn't empty.  This can happen, for example,
@@ -4392,172 +4394,23 @@
 			}
 		}
 
-		growing_buffer* order_buf = NULL;  // to collect ORDER BY list
-
 		// Build an ORDER BY clause, if there is one
 		if( NULL == order_hash )
 			;  // No ORDER BY? do nothing
 		else if( JSON_ARRAY == order_hash->type ) {
-			// Array of field specifications, each specification being a
-			// hash to define the class, field, and other details
-			int order_idx = 0;
-			jsonObject* order_spec;
-			while( (order_spec = jsonObjectGetIndex( order_hash, order_idx++ ) ) ) {
-
-				if( JSON_HASH != order_spec->type ) {
-					osrfLogError( OSRF_LOG_MARK,
-						 "%s: Malformed field specification in ORDER BY clause; expected JSON_HASH, found %s",
-						modulename, json_type( order_spec->type ) );
-					if( ctx )
-						osrfAppSessionStatus(
-							 ctx->session,
-							OSRF_STATUS_INTERNALSERVERERROR,
-							"osrfMethodException",
-							ctx->request,
-							"Malformed ORDER BY clause -- see error log for more details"
-						);
-					buffer_free( order_buf );
-					free( having_buf );
-					buffer_free( group_buf );
-					buffer_free( sql_buf );
-					if( defaultselhash )
-						jsonObjectFree( defaultselhash );
-					return NULL;
-				}
-
-				const char* class_alias =
-						jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "class" ) );
-				const char* field =
-						jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "field" ) );
-
-				if( order_buf )
-					OSRF_BUFFER_ADD( order_buf, ", " );
-				else
-					order_buf = buffer_init( 128 );
-
-				if( !field || !class_alias ) {
-					osrfLogError( OSRF_LOG_MARK,
-						"%s: Missing class or field name in field specification "
-						"of ORDER BY clause",
-						modulename );
-					if( ctx )
-						osrfAppSessionStatus(
-							ctx->session,
-							OSRF_STATUS_INTERNALSERVERERROR,
-							"osrfMethodException",
-							ctx->request,
-							"Malformed ORDER BY clause -- see error log for more details"
-						);
-					buffer_free( order_buf );
-					free( having_buf );
-					buffer_free( group_buf );
-					buffer_free( sql_buf );
-					if( defaultselhash )
-						jsonObjectFree( defaultselhash );
-					return NULL;
-				}
-
-				ClassInfo* order_class_info = search_alias( class_alias );
-				if( ! order_class_info ) {
-					osrfLogError( OSRF_LOG_MARK, "%s: ORDER BY clause references class \"%s\" "
-							"not in FROM clause", modulename, class_alias );
-					if( ctx )
-						osrfAppSessionStatus(
-							ctx->session,
-							OSRF_STATUS_INTERNALSERVERERROR,
-							"osrfMethodException",
-							ctx->request,
-							"Invalid class referenced in ORDER BY clause -- "
-							"see error log for more details"
-						);
-					free( having_buf );
-					buffer_free( group_buf );
-					buffer_free( sql_buf );
-					if( defaultselhash )
-						jsonObjectFree( defaultselhash );
-					return NULL;
-				}
-
-				osrfHash* field_def = osrfHashGet( order_class_info->fields, field );
-				if( !field_def ) {
-					osrfLogError( OSRF_LOG_MARK,
-						"%s: Invalid field \"%s\".%s referenced in ORDER BY clause",
-						modulename, class_alias, field );
-					if( ctx )
-						osrfAppSessionStatus(
-							ctx->session,
-							OSRF_STATUS_INTERNALSERVERERROR,
-							"osrfMethodException",
-							ctx->request,
-							"Invalid field referenced in ORDER BY clause -- "
-							"see error log for more details"
-						);
-					free( having_buf );
-					buffer_free( group_buf );
-					buffer_free( sql_buf );
-					if( defaultselhash )
-						jsonObjectFree( defaultselhash );
-					return NULL;
-				} else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
-					osrfLogError( OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
-								 modulename, field );
-					if( ctx )
-						osrfAppSessionStatus(
-							ctx->session,
-							OSRF_STATUS_INTERNALSERVERERROR,
-							"osrfMethodException",
-							ctx->request,
-							"Virtual field in ORDER BY clause -- see error log for more details"
-						);
-					buffer_free( order_buf );
-					free( having_buf );
-					buffer_free( group_buf );
-					buffer_free( sql_buf );
-					if( defaultselhash )
-						jsonObjectFree( defaultselhash );
-					return NULL;
-				}
-
-				if( jsonObjectGetKeyConst( order_spec, "transform" ) ) {
-					char* transform_str = searchFieldTransform(
-							class_alias, field_def, order_spec );
-					if( ! transform_str ) {
-						if( ctx )
-							osrfAppSessionStatus(
-								ctx->session,
-								OSRF_STATUS_INTERNALSERVERERROR,
-								"osrfMethodException",
-								ctx->request,
-								"Severe query error in ORDER BY clause -- "
-								"see error log for more details"
-							);
-						buffer_free( order_buf );
-						free( having_buf );
-						buffer_free( group_buf );
-						buffer_free( sql_buf );
-						if( defaultselhash )
-							jsonObjectFree( defaultselhash );
-						return NULL;
-					}
-
-					OSRF_BUFFER_ADD( order_buf, transform_str );
-					free( transform_str );
-				}
-				else
-					buffer_fadd( order_buf, "\"%s\".%s", class_alias, field );
-
-				const char* direction =
-						jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "direction" ) );
-				if( direction ) {
-					if( direction[ 0 ] || 'D' == direction[ 0 ] )
-						OSRF_BUFFER_ADD( order_buf, " DESC" );
-					else
-						OSRF_BUFFER_ADD( order_buf, " ASC" );
-				}
+			order_by_list = buildOrderByFromArray( ctx, order_hash );
+			if( !order_by_list ) {
+				free( having_buf );
+				buffer_free( group_buf );
+				buffer_free( sql_buf );
+				if( defaultselhash )
+					jsonObjectFree( defaultselhash );
+				return NULL;
 			}
 		} else if( JSON_HASH == order_hash->type ) {
 			// This hash is keyed on class alias.  Each class has either
 			// an array of field names or a hash keyed on field name.
+			growing_buffer* order_buf = NULL;  // to collect ORDER BY list
 			jsonIterator* class_itr = jsonNewIterator( order_hash );
 			while( (snode = jsonIteratorNext( class_itr )) ) {
 
@@ -4573,7 +4426,7 @@
 							"osrfMethodException",
 							ctx->request,
 							"Invalid class referenced in ORDER BY clause -- "
-							"see error log for more details"
+								"see error log for more details"
 						);
 					jsonIteratorFree( class_itr );
 					buffer_free( order_buf );
@@ -4820,6 +4673,8 @@
 				}
 			} // end while
 			jsonIteratorFree( class_itr );
+			if( order_buf )
+				order_by_list = buffer_release( order_buf );
 		} else {
 			osrfLogError( OSRF_LOG_MARK,
 				"%s: Malformed ORDER BY clause; expected JSON_HASH or JSON_ARRAY, found %s",
@@ -4832,7 +4687,6 @@
 					ctx->request,
 					"Malformed ORDER BY clause -- see error log for more details"
 				);
-			buffer_free( order_buf );
 			free( having_buf );
 			buffer_free( group_buf );
 			buffer_free( sql_buf );
@@ -4840,12 +4694,8 @@
 				jsonObjectFree( defaultselhash );
 			return NULL;
 		}
-
-		if( order_buf )
-			order_by_list = buffer_release( order_buf );
 	}
 
-
 	string = buffer_release( group_buf );
 
 	if( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
@@ -4891,26 +4741,199 @@
 
 } // end of SELECT()
 
-static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
+/**
+	@brief Build a list of ORDER BY expressions.
+	@param ctx Pointer to the method context.
+	@param order_array Pointer to a JSON_ARRAY of field specifications.
+	@return Pointer to a string containing a comma-separated list of ORDER BY expressions.
+	Each expression may be either a column reference or a function call whose first parameter
+	is a column reference.
 
+	Each entry in @a order_array must be a JSON_HASH with values for "class" and "field".
+	It may optionally include entries for "direction" and/or "transform".
+
+	The calling code is responsible for freeing the returned string.
+*/
+static char* buildOrderByFromArray( osrfMethodContext* ctx, const jsonObject* order_array ) {
+	if( ! order_array ) {
+		osrfLogError( OSRF_LOG_MARK, "%s: Logic error: NULL pointer for ORDER BY clause",
+			modulename );
+		if( ctx )
+			osrfAppSessionStatus(
+				ctx->session,
+				OSRF_STATUS_INTERNALSERVERERROR,
+				"osrfMethodException",
+				ctx->request,
+				"Logic error: ORDER BY clause expected, not found; "
+					"see error log for more details"
+			);
+		return NULL;
+	} else if( order_array->type != JSON_ARRAY ) {
+		osrfLogError( OSRF_LOG_MARK,
+			"%s: Logic error: Expected JSON_ARRAY for ORDER BY clause, not found", modulename );
+		if( ctx )
+			osrfAppSessionStatus(
+			ctx->session,
+			OSRF_STATUS_INTERNALSERVERERROR,
+			"osrfMethodException",
+			ctx->request,
+			"Logic error: Unexpected format for ORDER BY clause; see error log for more details" );
+		return NULL;
+	}
+
+	growing_buffer* order_buf = buffer_init( 128 );
+	int first = 1;        // boolean
+	int order_idx = 0;
+	jsonObject* order_spec;
+	while( (order_spec = jsonObjectGetIndex( order_array, order_idx++ ))) {
+
+		if( JSON_HASH != order_spec->type ) {
+			osrfLogError( OSRF_LOG_MARK,
+				"%s: Malformed field specification in ORDER BY clause; "
+				"expected JSON_HASH, found %s",
+				modulename, json_type( order_spec->type ) );
+			if( ctx )
+				osrfAppSessionStatus(
+					 ctx->session,
+					OSRF_STATUS_INTERNALSERVERERROR,
+					"osrfMethodException",
+					ctx->request,
+					"Malformed ORDER BY clause -- see error log for more details"
+				);
+			buffer_free( order_buf );
+			return NULL;
+		}
+
+		const char* class_alias =
+			jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "class" ));
+		const char* field =
+			jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "field" ));
+
+		if( !field || !class_alias ) {
+			osrfLogError( OSRF_LOG_MARK,
+				"%s: Missing class or field name in field specification of ORDER BY clause",
+				modulename );
+			if( ctx )
+				osrfAppSessionStatus(
+					ctx->session,
+					OSRF_STATUS_INTERNALSERVERERROR,
+					"osrfMethodException",
+					ctx->request,
+					"Malformed ORDER BY clause -- see error log for more details"
+				);
+			buffer_free( order_buf );
+			return NULL;
+		}
+
+		const ClassInfo* order_class_info = search_alias( class_alias );
+		if( ! order_class_info ) {
+			osrfLogInternal( OSRF_LOG_MARK, "%s: ORDER BY clause references class \"%s\" "
+				"not in FROM clause, skipping it", modulename, class_alias );
+			continue;
+		}
+
+		// Add a separating comma, except at the beginning
+		if( first )
+			first = 0;
+		else
+			OSRF_BUFFER_ADD( order_buf, ", " );
+
+		osrfHash* field_def = osrfHashGet( order_class_info->fields, field );
+		if( !field_def ) {
+			osrfLogError( OSRF_LOG_MARK,
+				"%s: Invalid field \"%s\".%s referenced in ORDER BY clause",
+				modulename, class_alias, field );
+			if( ctx )
+				osrfAppSessionStatus(
+					ctx->session,
+					OSRF_STATUS_INTERNALSERVERERROR,
+					"osrfMethodException",
+					ctx->request,
+					"Invalid field referenced in ORDER BY clause -- "
+					"see error log for more details"
+				);
+			free( order_buf );
+			return NULL;
+		} else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
+			osrfLogError( OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
+				modulename, field );
+			if( ctx )
+				osrfAppSessionStatus(
+					ctx->session,
+					OSRF_STATUS_INTERNALSERVERERROR,
+					"osrfMethodException",
+					ctx->request,
+					"Virtual field in ORDER BY clause -- see error log for more details"
+				);
+			buffer_free( order_buf );
+			return NULL;
+		}
+
+		if( jsonObjectGetKeyConst( order_spec, "transform" )) {
+			char* transform_str = searchFieldTransform( class_alias, field_def, order_spec );
+			if( ! transform_str ) {
+				if( ctx )
+					osrfAppSessionStatus(
+						ctx->session,
+						OSRF_STATUS_INTERNALSERVERERROR,
+						"osrfMethodException",
+						ctx->request,
+						"Severe query error in ORDER BY clause -- "
+						"see error log for more details"
+					);
+				buffer_free( order_buf );
+				return NULL;
+			}
+
+			OSRF_BUFFER_ADD( order_buf, transform_str );
+			free( transform_str );
+		}
+		else
+			buffer_fadd( order_buf, "\"%s\".%s", class_alias, field );
+
+		const char* direction =
+			jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "direction" ) );
+		if( direction ) {
+			if( direction[ 0 ] || 'D' == direction[ 0 ] )
+				OSRF_BUFFER_ADD( order_buf, " DESC" );
+			else
+				OSRF_BUFFER_ADD( order_buf, " ASC" );
+		}
+	}
+
+	return buffer_release( order_buf );
+}
+
+/**
+	@brief Build a SELECT statement.
+	@param search_hash Pointer to a JSON_HASH or JSON_ARRAY encoding the WHERE clause.
+	@param rest_of_query Pointer to a JSON_HASH containing any other SQL clauses.
+	@param meta Pointer to the class metadata for the core class.
+	@param ctx Pointer to the method context.
+	@return Pointer to a character string containing the WHERE clause; or NULL upon error.
+
+	Within the rest_of_query hash, the meaningful keys are "join", "select", "no_i18n",
+	"order_by", "limit", and "offset".
+
+	The SELECT statements built here are distinct from those built for the json_query method.
+*/
+static char* buildSELECT ( const jsonObject* search_hash, jsonObject* rest_of_query,
+	osrfHash* meta, osrfMethodContext* ctx ) {
+
 	const char* locale = osrf_message_get_last_locale();
 
 	osrfHash* fields = osrfHashGet( meta, "fields" );
-	char* core_class = osrfHashGet( meta, "classname" );
+	const char* core_class = osrfHashGet( meta, "classname" );
 
-	const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
+	const jsonObject* join_hash = jsonObjectGetKeyConst( rest_of_query, "join" );
 
-	jsonObject* node = NULL;
-	jsonObject* snode = NULL;
-	jsonObject* onode = NULL;
-	const jsonObject* _tmp = NULL;
 	jsonObject* selhash = NULL;
 	jsonObject* defaultselhash = NULL;
 
 	growing_buffer* sql_buf = buffer_init( 128 );
 	growing_buffer* select_buf = buffer_init( 128 );
 
-	if( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
+	if( !(selhash = jsonObjectGetKey( rest_of_query, "select" )) ) {
 		defaultselhash = jsonNewObjectType( JSON_HASH );
 		selhash = defaultselhash;
 	}
@@ -4932,20 +4955,24 @@
 		jsonObjectSetKey( selhash, core_class, field_list );
 	}
 
+	// Build a list of columns for the SELECT clause
 	int first = 1;
+	const jsonObject* snode = NULL;
 	jsonIterator* class_itr = jsonNewIterator( selhash );
-	while( (snode = jsonIteratorNext( class_itr )) ) {
+	while( (snode = jsonIteratorNext( class_itr )) ) {        // For each class
 
+		// If the class isn't in the IDL, ignore it
 		const char* cname = class_itr->key;
 		osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
 		if( !idlClass )
 			continue;
 
-		if( strcmp(core_class,class_itr->key )) {
+		// If the class isn't the core class, and isn't in the JOIN clause, ignore it
+		if( strcmp( core_class, class_itr->key )) {
 			if( !join_hash )
 				continue;
 
-			jsonObject* found =  jsonObjectFindPath( join_hash, "//%s", class_itr->key );
+			jsonObject* found = jsonObjectFindPath( join_hash, "//%s", class_itr->key );
 			if( !found->size ) {
 				jsonObjectFree( found );
 				continue;
@@ -4954,6 +4981,7 @@
 			jsonObjectFree( found );
 		}
 
+		const jsonObject* node = NULL;
 		jsonIterator* select_itr = jsonNewIterator( snode );
 		while( (node = jsonIteratorNext( select_itr )) ) {
 			const char* item_str = jsonObjectGetString( node );
@@ -4971,7 +4999,7 @@
 
 			if( locale ) {
 				const char* i18n;
-				const jsonObject* no_i18n_obj = jsonObjectGetKeyConst( order_hash, "no_i18n" );
+				const jsonObject* no_i18n_obj = jsonObjectGetKeyConst( rest_of_query, "no_i18n" );
 				if( obj_is_true( no_i18n_obj ) )    // Suppress internationalization?
 					i18n = NULL;
 				else
@@ -5019,9 +5047,13 @@
 				ctx->request,
 				"Unable to build query frame for core class"
 			);
+		buffer_free( sql_buf );
+		if( defaultselhash )
+			jsonObjectFree( defaultselhash );
 		return NULL;
 	}
 
+	// Add the JOIN clauses, if any
 	if( join_hash ) {
 		char* join_clause = searchJOIN( join_hash, &curr_query->core );
 		OSRF_BUFFER_ADD_CHAR( sql_buf, ' ' );
@@ -5030,10 +5062,11 @@
 	}
 
 	osrfLogDebug( OSRF_LOG_MARK, "%s pre-predicate SQL =  %s",
-				  modulename, OSRF_BUFFER_C_STR( sql_buf ));
+		modulename, OSRF_BUFFER_C_STR( sql_buf ));
 
 	OSRF_BUFFER_ADD( sql_buf, " WHERE " );
 
+	// Add the conditions in the WHERE clause
 	char* pred = searchWHERE( search_hash, &curr_query->core, AND_OP_JOIN, ctx );
 	if( !pred ) {
 		osrfAppSessionStatus(
@@ -5053,112 +5086,166 @@
 		free( pred );
 	}
 
-	if( order_hash ) {
-		char* string = NULL;
-		if( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
+	// Add the ORDER BY, LIMIT, and/or OFFSET clauses, if present
+	if( rest_of_query ) {
+		const jsonObject* order_by = NULL;
+		if( ( order_by = jsonObjectGetKeyConst( rest_of_query, "order_by" )) ){
 
-			growing_buffer* order_buf = buffer_init( 128 );
+			char* order_by_list = NULL;
 
-			first = 1;
-			jsonIterator* class_itr = jsonNewIterator( _tmp );
-			while( (snode = jsonIteratorNext( class_itr )) ) {
+			if( JSON_ARRAY == order_by->type ) {
+				order_by_list = buildOrderByFromArray( ctx, order_by );
+				if( !order_by_list ) {
+					buffer_free( sql_buf );
+					if( defaultselhash )
+						jsonObjectFree( defaultselhash );
+					clear_query_stack();
+					return NULL;
+				}
+			} else if( JSON_HASH == order_by->type ) {
+				// We expect order_by to be a JSON_HASH keyed on class names.  Traverse it
+				// and build a list of ORDER BY expressions.
+				growing_buffer* order_buf = buffer_init( 128 );
+				first = 1;
+				jsonIterator* class_itr = jsonNewIterator( order_by );
+				while( (snode = jsonIteratorNext( class_itr )) ) {  // For each class:
 
-				if( !jsonObjectGetKeyConst( selhash,class_itr->key ))
-					continue;
+					ClassInfo* order_class_info = search_alias( class_itr->key );
+					if( ! order_class_info )
+						continue;    // class not referenced by FROM clause?  Ignore it.
 
-				if( snode->type == JSON_HASH ) {
+					if( JSON_HASH == snode->type ) {
 
-					jsonIterator* order_itr = jsonNewIterator( snode );
-					while( (onode = jsonIteratorNext( order_itr )) ) {
+						// If the data for the current class is a JSON_HASH, then it is
+						// keyed on field name.
 
-						osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s",
-								class_itr->key, order_itr->key );
-						if( !field_def )
-							continue;
+						const jsonObject* onode = NULL;
+						jsonIterator* order_itr = jsonNewIterator( snode );
+						while( (onode = jsonIteratorNext( order_itr )) ) {  // For each field
 
-						char* direction = NULL;
-						if( onode->type == JSON_HASH ) {
-							if( jsonObjectGetKeyConst( onode, "transform" ) ) {
-								string = searchFieldTransform( class_itr->key, field_def, onode );
-								if( ! string ) {
-									osrfAppSessionStatus(
-										ctx->session,
-										OSRF_STATUS_INTERNALSERVERERROR,
-										"osrfMethodException",
-										ctx->request,
-										"Severe query error in ORDER BY clause -- "
-										"see error log for more details"
-									);
-									jsonIteratorFree( order_itr );
-									jsonIteratorFree( class_itr );
-									buffer_free( order_buf );
-									buffer_free( sql_buf );
-									if( defaultselhash )
-										jsonObjectFree( defaultselhash );
-									clear_query_stack();
-									return NULL;
+							osrfHash* field_def = osrfHashGet(
+								order_class_info->fields, order_itr->key );
+							if( !field_def )
+								continue;    // Field not defined in IDL?  Ignore it.
+							if( str_is_true( osrfHashGet( field_def, "virtual")))
+								continue;    // Field is virtual?  Ignore it.
+
+							char* field_str = NULL;
+							char* direction = NULL;
+							if( onode->type == JSON_HASH ) {
+								if( jsonObjectGetKeyConst( onode, "transform" ) ) {
+									field_str = searchFieldTransform(
+										class_itr->key, field_def, onode );
+									if( ! field_str ) {
+										osrfAppSessionStatus(
+											ctx->session,
+											OSRF_STATUS_INTERNALSERVERERROR,
+											"osrfMethodException",
+											ctx->request,
+											"Severe query error in ORDER BY clause -- "
+											"see error log for more details"
+										);
+										jsonIteratorFree( order_itr );
+										jsonIteratorFree( class_itr );
+										buffer_free( order_buf );
+										buffer_free( sql_buf );
+										if( defaultselhash )
+											jsonObjectFree( defaultselhash );
+										clear_query_stack();
+										return NULL;
+									}
+								} else {
+									growing_buffer* field_buf = buffer_init( 16 );
+									buffer_fadd( field_buf, "\"%s\".%s",
+										class_itr->key, order_itr->key );
+									field_str = buffer_release( field_buf );
 								}
+
+								if( ( order_by = jsonObjectGetKeyConst( onode, "direction" )) ) {
+									const char* dir = jsonObjectGetString( order_by );
+									if(!strncasecmp( dir, "d", 1 )) {
+										direction = " DESC";
+									}
+								}
 							} else {
-								growing_buffer* field_buf = buffer_init( 16 );
-								buffer_fadd( field_buf, "\"%s\".%s",
-									class_itr->key, order_itr->key );
-								string = buffer_release( field_buf );
-							}
-
-							if( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
-								const char* dir = jsonObjectGetString( _tmp );
-								if(!strncasecmp( dir, "d", 1 )) {
+								field_str = strdup( order_itr->key );
+								const char* dir = jsonObjectGetString( onode );
+								if( !strncasecmp( dir, "d", 1 )) {
 									direction = " DESC";
+								} else {
+									direction = " ASC";
 								}
 							}
-						} else {
-							string = strdup( order_itr->key );
-							const char* dir = jsonObjectGetString( onode );
-							if( !strncasecmp( dir, "d", 1 )) {
-								direction = " DESC";
+
+							if( first ) {
+								first = 0;
 							} else {
-								direction = " ASC";
+								buffer_add( order_buf, ", " );
 							}
-						}
 
-						if( first ) {
-							first = 0;
-						} else {
-							buffer_add( order_buf, ", " );
-						}
+							buffer_add( order_buf, field_str );
+							free( field_str );
 
-						buffer_add( order_buf, string );
-						free( string );
+							if( direction ) {
+								buffer_add( order_buf, direction );
+							}
+						} // end while; looping over ORDER BY expressions
 
-						if( direction ) {
-							buffer_add( order_buf, direction );
+						jsonIteratorFree( order_itr );
+
+					} else if( JSON_STRING == snode->type ) {
+						// We expect a comma-separated list of sort fields.
+						const char* str = jsonObjectGetString( snode );
+						if( strchr( str, ';' )) {
+							// No semicolons allowed.  It is theoretically possible for a
+							// legitimate semicolon to occur within quotes, but it's not likely
+							// to occur in practice in the context of an ORDER BY list.
+							osrfLogError( OSRF_LOG_MARK, "%s: Possible attempt at SOL injection -- "
+								"semicolon found in ORDER BY list: \"%s\"", modulename, str );
+							if( ctx ) {
+								osrfAppSessionStatus(
+									ctx->session,
+									OSRF_STATUS_INTERNALSERVERERROR,
+									"osrfMethodException",
+									ctx->request,
+									"Possible attempt at SOL injection -- "
+										"semicolon found in ORDER BY list"
+								);
+							}
+							jsonIteratorFree( class_itr );
+							buffer_free( order_buf );
+							buffer_free( sql_buf );
+							if( defaultselhash )
+								jsonObjectFree( defaultselhash );
+							clear_query_stack();
+							return NULL;
 						}
+						buffer_add( order_buf, str );
+						break;
 					}
 
-					jsonIteratorFree( order_itr );
+				} // end while; looping over order_by classes
 
-				} else {
-					const char* str = jsonObjectGetString( snode );
-					buffer_add( order_buf, str );
-					break;
-				}
+				jsonIteratorFree( class_itr );
+				order_by_list = buffer_release( order_buf );
 
+			} else {
+				osrfLogWarning( OSRF_LOG_MARK,
+					"\"order_by\" object in a query is not a JSON_HASH or JSON_ARRAY;"
+					"no ORDER BY generated" );
 			}
 
-			jsonIteratorFree( class_itr );
-
-			string = buffer_release( order_buf );
-
-			if( *string ) {
+			if( order_by_list && *order_by_list ) {
 				OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
-				OSRF_BUFFER_ADD( sql_buf, string );
+				OSRF_BUFFER_ADD( sql_buf, order_by_list );
 			}
 
-			free( string );
+			free( order_by_list );
 		}
 
-		if( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ) {
-			const char* str = jsonObjectGetString( _tmp );
+		const jsonObject* limit = jsonObjectGetKeyConst( rest_of_query, "limit" );
+		if( limit ) {
+			const char* str = jsonObjectGetString( limit );
 			buffer_fadd(
 				sql_buf,
 				" LIMIT %d",
@@ -5166,9 +5253,9 @@
 			);
 		}
 
-		_tmp = jsonObjectGetKeyConst( order_hash, "offset" );
-		if( _tmp ) {
-			const char* str = jsonObjectGetString( _tmp );
+		const jsonObject* offset = jsonObjectGetKeyConst( rest_of_query, "offset" );
+		if( offset ) {
+			const char* str = jsonObjectGetString( offset );
 			buffer_fadd(
 				sql_buf,
 				" OFFSET %d",

Copied: branches/serials-integration/Open-ILS/src/edi_translator/install.RHEL.sh (from rev 18366, trunk/Open-ILS/src/edi_translator/install.RHEL.sh)
===================================================================
--- branches/serials-integration/Open-ILS/src/edi_translator/install.RHEL.sh	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/edi_translator/install.RHEL.sh	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# JEDI converter scripts installation
+#
+# RHEL/CENTOS Install
+# note: need older version of rubygems since RHEL package for ruby is old
+#
+# run this script as root or with sudo
+
+yum install ruby ruby-devel ruby-rdoc
+
+wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.5.tgz
+tar zxvf rubygems-1.3.5.tgz
+pushd rubygems-1.3.5
+ruby setup.rb        # this gives harmless errors about README files missing
+gem install rubygems-update
+update_rubygems
+popd
+
+# RHEL has a bug in json module, but mbklein moved to using yajl for us....
+gem install parseconfig rspec edi4r edi4r-tdid rcov openils-mapper # mkmf
+

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Lineitem.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -16,10 +16,10 @@
 
 
 __PACKAGE__->register_method(
-	method => 'create_lineitem',
-	api_name	=> 'open-ils.acq.lineitem.create',
-	signature => {
-        desc => 'Creates a lineitem',
+    method    => 'create_lineitem',
+    api_name  => 'open-ils.acq.lineitem.create',
+    signature => {
+        desc   => 'Creates a lineitem',
         params => [
             {desc => 'Authentication token', type => 'string'},
             {desc => 'The lineitem object to create', type => 'object'},
@@ -67,10 +67,10 @@
 
 
 __PACKAGE__->register_method(
-	method => 'retrieve_lineitem',
-	api_name	=> 'open-ils.acq.lineitem.retrieve',
-	signature => {
-        desc => 'Retrieves a lineitem',
+    method    => 'retrieve_lineitem',
+    api_name  => 'open-ils.acq.lineitem.retrieve',
+    signature => {
+        desc   => 'Retrieves a lineitem',
         params => [
             {desc => 'Authentication token',    type => 'string'},
             {desc => 'lineitem ID to retrieve', type => 'number'},
@@ -149,15 +149,15 @@
         }
     }
 
-    return $e->event unless (
+    return $e->event unless ((
         $li->purchase_order and 
             ($no_auth or $e->allowed(['VIEW_PURCHASE_ORDER', 'CREATE_PURCHASE_ORDER'], 
                 $li->purchase_order->ordering_agency, $li->purchase_order))
-    ) or (
+        ) or (
         $li->picklist and !$li->purchase_order and # user doesn't have view_po perms
             ($no_auth or $e->allowed(['VIEW_PICKLIST', 'CREATE_PICKLIST'], 
                 $li->picklist->org_unit, $li->picklist))
-    );
+    ));
 
     unless ($$options{flesh_po}) {
         $li->purchase_order(
@@ -173,12 +173,12 @@
 
 
 __PACKAGE__->register_method(
-	method => 'delete_lineitem',
-	api_name	=> 'open-ils.acq.lineitem.delete',
-	signature => {
-        desc => 'Deletes a lineitem',
+    method    => 'delete_lineitem',
+    api_name  => 'open-ils.acq.lineitem.delete',
+    signature => {
+        desc   => 'Deletes a lineitem',
         params => [
-            {desc => 'Authentication token', type => 'string'},
+            {desc => 'Authentication token',  type => 'string'},
             {desc => 'lineitem ID to delete', type => 'number'},
         ],
         return => {desc => '1 on success, Event on error'}
@@ -221,12 +221,12 @@
 
 
 __PACKAGE__->register_method(
-	method => 'update_lineitem',
-	api_name	=> 'open-ils.acq.lineitem.update',
-	signature => {
-        desc => 'Update one or many lineitems',
+    method    => 'update_lineitem',
+    api_name  => 'open-ils.acq.lineitem.update',
+    signature => {
+        desc   => 'Update one or many lineitems',
         params => [
-            {desc => 'Authentication token', type => 'string'},
+            {desc => 'Authentication token',   type => 'string'},
             {desc => 'lineitem object update', type => 'object'}
         ],
         return => {desc => '1 on success, Event on error'}

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1581,12 +1581,12 @@
 
 
 __PACKAGE__->register_method(
-	method => 'lineitem_detail_CUD_batch_api',
-	api_name => 'open-ils.acq.lineitem_detail.cud.batch',
-    stream => 1,
-	signature => {
-        desc => q/Creates a new purchase order line item detail.  
-            Additionally creates the associated fund_debit/,
+    method    => 'lineitem_detail_CUD_batch_api',
+    api_name  => 'open-ils.acq.lineitem_detail.cud.batch',
+    stream    => 1,
+    signature => {
+        desc   => q/Creates a new purchase order line item detail. / .
+                  q/Additionally creates the associated fund_debit/,
         params => [
             {desc => 'Authentication token', type => 'string'},
             {desc => 'List of lineitem_details to create', type => 'array'},
@@ -1597,9 +1597,9 @@
 );
 
 __PACKAGE__->register_method(
-	method => 'lineitem_detail_CUD_batch_api',
-	api_name => 'open-ils.acq.lineitem_detail.cud.batch.dry_run',
-    stream => 1,
+    method    => 'lineitem_detail_CUD_batch_api',
+    api_name  => 'open-ils.acq.lineitem_detail.cud.batch.dry_run',
+    stream    => 1,
     signature => { 
         desc => q/
             Dry run version of open-ils.acq.lineitem_detail.cud.batch.
@@ -1691,8 +1691,8 @@
 
 
 __PACKAGE__->register_method(
-	method => 'receive_po_api',
-	api_name	=> 'open-ils.acq.purchase_order.receive'
+    method   => 'receive_po_api',
+    api_name => 'open-ils.acq.purchase_order.receive'
 );
 
 sub receive_po_api {
@@ -1930,13 +1930,13 @@
 }
 
 __PACKAGE__->register_method(
-	method => 'rollback_receive_lineitem_api',
-	api_name	=> 'open-ils.acq.lineitem.receive.rollback',
-	signature => {
-        desc => 'Mark a lineitem as Un-received',
+    method    => 'rollback_receive_lineitem_api',
+    api_name  => 'open-ils.acq.lineitem.receive.rollback',
+    signature => {
+        desc   => 'Mark a lineitem as Un-received',
         params => [
             {desc => 'Authentication token', type => 'string'},
-            {desc => 'lineitem ID', type => 'number'}
+            {desc => 'lineitem ID',          type => 'number'}
         ],
         return => {desc =>
             "on success, object describing changes to LI and possibly PO; " .
@@ -1975,13 +1975,13 @@
 
 
 __PACKAGE__->register_method(
-	method => 'set_lineitem_price_api',
-	api_name	=> 'open-ils.acq.lineitem.price.set',
-	signature => {
-        desc => 'Set lineitem price.  If debits already exist, update them as well',
+    method    => 'set_lineitem_price_api',
+    api_name  => 'open-ils.acq.lineitem.price.set',
+    signature => {
+        desc   => 'Set lineitem price.  If debits already exist, update them as well',
         params => [
             {desc => 'Authentication token', type => 'string'},
-            {desc => 'lineitem ID', type => 'number'}
+            {desc => 'lineitem ID',          type => 'number'}
         ],
         return => {desc => 'status blob, Event on error'}
     }
@@ -2024,10 +2024,10 @@
 
 
 __PACKAGE__->register_method(
-	method => 'clone_picklist_api',
-	api_name	=> 'open-ils.acq.picklist.clone',
-	signature => {
-        desc => 'Clones a picklist, including lineitem and lineitem details',
+    method    => 'clone_picklist_api',
+    api_name  => 'open-ils.acq.picklist.clone',
+    signature => {
+        desc   => 'Clones a picklist, including lineitem and lineitem details',
         params => [
             {desc => 'Authentication token', type => 'string'},
             {desc => 'Picklist ID', type => 'number'},
@@ -2072,10 +2072,10 @@
 
 
 __PACKAGE__->register_method(
-	method => 'merge_picklist_api',
-	api_name	=> 'open-ils.acq.picklist.merge',
-	signature => {
-        desc => 'Merges 2 or more picklists into a single list',
+    method    => 'merge_picklist_api',
+    api_name  => 'open-ils.acq.picklist.merge',
+    signature => {
+        desc   => 'Merges 2 or more picklists into a single list',
         params => [
             {desc => 'Authentication token', type => 'string'},
             {desc => 'Lead Picklist ID', type => 'number'},
@@ -2119,13 +2119,13 @@
 
 
 __PACKAGE__->register_method(
-	method => 'delete_picklist_api',
-	api_name	=> 'open-ils.acq.picklist.delete',
-	signature => {
-        desc => q/Deletes a picklist.  It also deletes any lineitems in the "new" state.  
-            Other attached lineitems are detached'/,
+    method    => 'delete_picklist_api',
+    api_name  => 'open-ils.acq.picklist.delete',
+    signature => {
+        desc   => q/Deletes a picklist.  It also deletes any lineitems in the "new" state. / .
+                  q/Other attached lineitems are detached/,
         params => [
-            {desc => 'Authentication token', type => 'string'},
+            {desc => 'Authentication token',  type => 'string'},
             {desc => 'Picklist ID to delete', type => 'number'}
         ],
         return => {desc => '1 on success, Event on error'}
@@ -2146,17 +2146,16 @@
 
 
 __PACKAGE__->register_method(
-	method => 'activate_purchase_order',
-	api_name	=> 'open-ils.acq.purchase_order.activate.dry_run'
+    method   => 'activate_purchase_order',
+    api_name => 'open-ils.acq.purchase_order.activate.dry_run'
 );
 
 __PACKAGE__->register_method(
-	method => 'activate_purchase_order',
-	api_name	=> 'open-ils.acq.purchase_order.activate',
-	signature => {
-        desc => q/Activates a purchase order.  This updates the status of the PO
-            and Lineitems to 'on-order'.  Activated PO's are ready for EDI delivery
-            if appropriate./,
+    method    => 'activate_purchase_order',
+    api_name  => 'open-ils.acq.purchase_order.activate',
+    signature => {
+        desc => q/Activates a purchase order.  This updates the status of the PO / .
+                q/and Lineitems to 'on-order'.  Activated PO's are ready for EDI delivery if appropriate./,
         params => [
             {desc => 'Authentication token', type => 'string'},
             {desc => 'Purchase ID', type => 'number'}
@@ -2238,21 +2237,21 @@
     }
 
     # tell the world we activated a PO
-    $U->create_events_for_hook('acqpo.activated', $po, $po->ordering_agency);
+    $U->create_events_for_hook('acqpo.activated', $po, $po->ordering_agency) unless $dry_run;
 
     return undef;
 }
 
 
 __PACKAGE__->register_method(
-	method => 'split_purchase_order_by_lineitems',
-	api_name	=> 'open-ils.acq.purchase_order.split_by_lineitems',
-	signature => {
-        desc => q/Splits a PO into many POs, 1 per lineitem.  Only works for
-        POs a) with more than one lineitems, and b) in the "pending" state./,
+    method    => 'split_purchase_order_by_lineitems',
+    api_name  => 'open-ils.acq.purchase_order.split_by_lineitems',
+    signature => {
+        desc   => q/Splits a PO into many POs, 1 per lineitem.  Only works for / .
+                  q/POs a) with more than one lineitems, and b) in the "pending" state./,
         params => [
             {desc => 'Authentication token', type => 'string'},
-            {desc => 'Purchase order ID', type => 'number'}
+            {desc => 'Purchase order ID',    type => 'number'}
         ],
         return => {desc => 'list of new PO IDs on success, Event on error'}
     }

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -174,6 +174,7 @@
 
 __PACKAGE__->register_method(
     method   => "user_settings",
+    authoritative => 1,
     api_name => "open-ils.actor.patron.settings.retrieve",
 );
 sub user_settings {
@@ -1275,10 +1276,11 @@
     my $api = $self->api_name;
 
     if( $api =~ /password/o ) {
-
         # make sure the original password matches the in-database password
-        return OpenILS::Event->new('INCORRECT_PASSWORD')
-            if md5_hex($orig_pw) ne $db_user->passwd;
+        if (md5_hex($orig_pw) ne $db_user->passwd) {
+            $e->rollback;
+            return new OpenILS::Event('INCORRECT_PASSWORD');
+        }
         $db_user->passwd($new_val);
 
     } else {
@@ -1291,7 +1293,10 @@
 
             # make sure no one else has this username
             my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1}); 
-			return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
+            if (@$exist) {
+                $e->rollback;
+                return new OpenILS::Event('USERNAME_EXISTS');
+            }
             $db_user->usrname($new_val);
 
         } elsif( $api =~ /email/o ) {
@@ -2477,13 +2482,13 @@
 sub update_user_note {
 	my( $self, $conn, $auth, $note ) = @_;
 	my $e = new_editor(authtoken=>$auth, xact=>1);
-	return $e->event unless $e->checkauth;
+	return $e->die_event unless $e->checkauth;
 	my $patron = $e->retrieve_actor_user($note->usr)
-		or return $e->event;
-	return $e->event unless 
+		or return $e->die_event;
+	return $e->die_event unless 
 		$e->allowed('UPDATE_USER', $patron->home_ou);
 	$e->update_actor_user_note($note)
-		or return $e->event;
+		or return $e->die_event;
 	$e->commit;
 	return 1;
 }
@@ -2883,7 +2888,7 @@
          	"flesh_fields" =>  { "au" => $fields }
       	}
    	]
-	) or return $e->event;
+	) or return $e->die_event;
 
 
 	if( grep { $_ eq 'addresses' } @$fields ) {
@@ -3258,7 +3263,7 @@
 sub apply_friend_perms {
     my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
     my $e = new_editor(authtoken => $auth, xact => 1);
-    return $e->event unless $e->checkauth;
+    return $e->die_event unless $e->checkauth;
 
     if($user_id != $e->requestor->id) {
         my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
@@ -3285,7 +3290,7 @@
 sub update_user_pending_address {
     my($self, $conn, $auth, $addr) = @_;
     my $e = new_editor(authtoken => $auth, xact => 1);
-    return $e->event unless $e->checkauth;
+    return $e->die_event unless $e->checkauth;
 
     if($addr->usr != $e->requestor->id) {
         my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1176,6 +1176,35 @@
 }
 
 
+sub patientreq {
+    my ($self, $client, $service, $method, @params) = @_;
+    my ($response, $err);
+
+    my $session = create OpenSRF::AppSession($service);
+    my $request = $session->request($method, @params);
+
+    my $spurt = 10;
+    my $give_up = time + 1000;
+
+    try {
+        while (time < $give_up) {
+            $response = $request->recv("timeout" => $spurt);
+            last if $request->complete;
+
+            $client->status(new OpenSRF::DomainObject::oilsContinueStatus);
+        }
+    } catch Error with {
+        $err = shift;
+    };
+
+    if ($err) {
+        warn "received error : service=$service : method=$method : params=".Dumper(\@params) . "\n $err";
+        throw $err ("Call to $service for method $method \n failed with exception: $err : " );
+    }
+
+    return $response->content;
+}
+
 # This logic now lives in storage
 sub __patron_money_owed {
 	my( $self, $patronid ) = @_;
@@ -1596,7 +1625,7 @@
 # most appropriate event.  create the event, fire it, then return the resulting
 # event with fleshed template_output and error_output
 sub fire_object_event {
-    my($self, $event_def, $hook, $object, $context_org, $granularity, $user_data) = @_;
+    my($self, $event_def, $hook, $object, $context_org, $granularity, $user_data, $client) = @_;
 
     my $e = OpenILS::Utils::CStoreEditor->new;
     my $def;
@@ -1616,6 +1645,8 @@
             or return $e->event;
     }
 
+    my $final_resp;
+
     if($def->group_field) {
         # we have a list of objects
         $object = [$object] unless ref $object eq 'ARRAY';
@@ -1632,37 +1663,74 @@
 
         $logger->info("EVENTS = " . OpenSRF::Utils::JSON->perl2JSON(\@event_ids));
 
-        my $resp = $self->simplereq(
-            'open-ils.trigger', 
-            'open-ils.trigger.event_group.fire',
-            \@event_ids);
+        my $resp;
+        if (not defined $client) {
+            $resp = $self->simplereq(
+                'open-ils.trigger',
+                'open-ils.trigger.event_group.fire',
+                \@event_ids);
+        } else {
+            $resp = $self->patientreq(
+                $client,
+                "open-ils.trigger", "open-ils.trigger.event_group.fire",
+                \@event_ids
+            );
+        }
 
-        return undef unless $resp and $resp->{events} and @{$resp->{events}};
+        if($resp and $resp->{events} and @{$resp->{events}}) {
 
-        return $e->retrieve_action_trigger_event([
-            $resp->{events}->[0]->id,
-            {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
-        ]);
+            $e->xact_begin;
+            $final_resp = $e->retrieve_action_trigger_event([
+                $resp->{events}->[0]->id,
+                {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
+            ]);
+            $e->rollback;
+        }
 
     } else {
 
         $object = $$object[0] if ref $object eq 'ARRAY';
 
-        my $event_id = $self->simplereq(
-            'open-ils.trigger', $auto_method, $def->id, $object, $context_org, $user_data);
+        my $event_id;
+        my $resp;
 
-        my $resp = $self->simplereq(
-            'open-ils.trigger', 
-            'open-ils.trigger.event.fire', 
-            $event_id);
+        if (not defined $client) {
+            $event_id = $self->simplereq(
+                'open-ils.trigger',
+                $auto_method, $def->id, $object, $context_org, $user_data
+            );
 
-        return undef unless $resp and $resp->{event};
+            $resp = $self->simplereq(
+                'open-ils.trigger',
+                'open-ils.trigger.event.fire',
+                $event_id
+            );
+        } else {
+            $event_id = $self->patientreq(
+                $client,
+                'open-ils.trigger',
+                $auto_method, $def->id, $object, $context_org, $user_data
+            );
 
-        return $e->retrieve_action_trigger_event([
-            $resp->{event}->id,
-            {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
-        ]);
+            $resp = $self->patientreq(
+                $client,
+                'open-ils.trigger',
+                'open-ils.trigger.event.fire',
+                $event_id
+            );
+        }
+        
+        if($resp and $resp->{event}) {
+            $e->xact_begin;
+            $final_resp = $e->retrieve_action_trigger_event([
+                $resp->{event}->id,
+                {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
+            ]);
+            $e->rollback;
+        }
     }
+
+    return $final_resp;
 }
 
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1291,12 +1291,13 @@
     }) or return $e->die_event;
 
     if (@$rows < 1) {
+        $e->rollback;
         return $rows;
     } else {
         # More than one result might be possible, but we don't want to return
         # more than one at this time.
         my $id = $rows->[0]->{"id"};
-        return $e->retrieve_booking_reservation([
+        my $resp =$e->retrieve_booking_reservation([
             $id, {
                 "flesh" => 2,
                 "flesh_fields" => {
@@ -1305,6 +1306,8 @@
                 }
             }
         ]) or $e->die_event;
+        $e->rollback;
+        return $resp;
     }
 }
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Cat.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -214,6 +214,116 @@
 }
 
 __PACKAGE__->register_method(
+	method	=> "template_overlay_container",
+	api_name	=> "open-ils.cat.container.template_overlay",
+    stream  => 1,
+    signature => q#
+        Overlays biblio.record_entry MARC values
+        @param auth The authtoken
+        @param container The container, um, containing the records to be updated by the template
+        @param template The overlay template, or nothing and the method will look for a negative bib id in the container
+        @return Stream of hashes record id in the key "record" and t or f for the success of the overlay operation in key "success"
+    #
+);
+
+__PACKAGE__->register_method(
+	method	=> "template_overlay_container",
+	api_name	=> "open-ils.cat.container.template_overlay.background",
+    stream  => 1,
+    signature => q#
+        Overlays biblio.record_entry MARC values
+        @param auth The authtoken
+        @param container The container, um, containing the records to be updated by the template
+        @param template The overlay template, or nothing and the method will look for a negative bib id in the container
+        @return Cache key to check for status of the container overlay
+    #
+);
+
+sub template_overlay_container {
+    my($self, $conn, $auth, $container, $template) = @_;
+    my $e = new_editor(authtoken=>$auth, xact=>1);
+    return $e->die_event unless $e->checkauth;
+
+    my $actor = OpenSRF::AppSession->create('open-ils.actor') if ($self->api_name =~ /background$/);
+
+    my $items = $e->search_container_biblio_record_entry_bucket_item({ bucket => $container });
+
+    my $titem;
+    if (!$template) {
+        ($titem) = grep { $_->target_biblio_record_entry < 0 } @$items;
+        if (!$titem) {
+            $e->rollback;
+            return undef;
+        }
+        $items = [grep { $_->target_biblio_record_entry > 0 } @$items];
+
+        $template = $e->retrieve_biblio_record_entry( $titem->target_biblio_record_entry )->marc;
+    }
+
+    my $responses = [];
+    my $some_failed = 0;
+
+    $self->respond_complete(
+        $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses)->gather(1)
+    ) if ($actor);
+
+    for my $item ( @$items ) {
+        my $rec = $e->retrieve_biblio_record_entry($item->target_biblio_record_entry);
+        next unless $rec;
+
+        my $success = 'f';
+        if ($e->allowed('UPDATE_RECORD', $rec->owner, $rec)) {
+            $success = $e->json_query(
+                { from => [ 'vandelay.template_overlay_bib_record', $template, $rec->id ] }
+            )->[0]->{'vandelay.template_overlay_bib_record'};
+        }
+
+        $some_failed++ if ($success eq 'f');
+
+        if ($actor) {
+            push @$responses, { record => $rec->id, success => $success };
+            $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+        } else {
+            $conn->respond({ record => $rec->id, success => $success });
+        }
+
+        if ($success eq 't') {
+            unless ($e->delete_container_biblio_record_entry_bucket_item($item)) {
+                $e->rollback;
+                if ($actor) {
+                    push @$responses, { complete => 1, success => 'f' };
+                    $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+                    return undef;
+                } else {
+                    return { complete => 1, success => 'f' };
+                }
+            }
+        }
+    }
+
+    if ($titem && !$some_failed) {
+        return $e->die_event unless ($e->delete_container_biblio_record_entry_bucket_item($titem));
+    }
+
+    if ($e->commit) {
+        if ($actor) {
+            push @$responses, { complete => 1, success => 't' };
+            $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+        } else {
+            return { complete => 1, success => 't' };
+        }
+    } else {
+        if ($actor) {
+            push @$responses, { complete => 1, success => 'f' };
+            $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+        } else {
+            return { complete => 1, success => 'f' };
+        }
+    }
+    return undef;
+}
+
+__PACKAGE__->register_method(
 	method	=> "update_biblio_record_entry",
 	api_name	=> "open-ils.cat.biblio.record_entry.update",
     signature => q/

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1196,7 +1196,8 @@
         max_fine_rule => $max_fine_rule->name,
         max_fine => $self->get_max_fine_amount($max_fine_rule),
         fine_interval => $recurring_fine_rule->recurrence_interval,
-        renewal_remaining => $duration_rule->max_renewals
+        renewal_remaining => $duration_rule->max_renewals,
+        duration_date_ceiling => $duration_rule->date_ceiling
     };
 
     $policy->{duration} = $duration_rule->shrt
@@ -1760,10 +1761,12 @@
    my $recurring  = $self->recurring_fines_rule;
    my $copy       = $self->copy;
    my $patron     = $self->patron;
+   my $duration_date_ceiling;
 
     if( $duration ) {
 
         my $policy = $self->get_circ_policy($duration, $recurring, $max);
+        $duration_date_ceiling = $policy->{duration_date_ceiling};
 
         my $dname = $duration->name;
         my $mname = $max->name;
@@ -1816,7 +1819,7 @@
 
     # if a patron is renewing, 'requestor' will be the patron
     $circ->circ_staff($self->editor->requestor->id);
-    $circ->due_date( $self->create_due_date($circ->duration) ) if $circ->duration;
+    $circ->due_date( $self->create_due_date($circ->duration, $duration_date_ceiling) ) if $circ->duration;
 
     $self->circ($circ);
 }
@@ -2016,7 +2019,7 @@
 
 
 sub create_due_date {
-    my( $self, $duration ) = @_;
+    my( $self, $duration, $date_ceiling ) = @_;
 
     # if there is a raw time component (e.g. from postgres), 
     # turn it into an interval that interval_to_seconds can parse
@@ -2028,6 +2031,14 @@
     # add the circ duration
     $due_date->add(seconds => OpenSRF::Utils->interval_to_seconds($duration));
 
+    if($date_ceiling) {
+        my $cdate = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($date_ceiling));
+        if ($cdate > DateTime->now and $cdate < $due_date) {
+            $logger->info("circulator: overriding due date with date ceiling: $date_ceiling");
+            $due_date = $cdate;
+        }
+    }
+
     # return ISO8601 time with timezone
     return $due_date->strftime('%FT%T%z');
 }
@@ -2323,7 +2334,6 @@
             $self->update_copy;
         }
     }
-    $logger->info("LFW XXX: way down here"); # LFW XXX
 
     if($self->claims_never_checked_out and 
             $U->ou_ancestor_setting_value($self->circ->circ_lib, 'circ.claim_never_checked_out.mark_missing')) {
@@ -2682,7 +2692,7 @@
             return;
         } 
 
-        $logger->warn("circulator:  * hold notify failed for hold $holdid");
+        $logger->debug("circulator:  * hold notify cancelled or failed for hold $holdid");
 
     } else {
         $logger->info("circulator: Not sending hold notification since the patron has no email address");

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/CopyLocations.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/CopyLocations.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/CopyLocations.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -182,7 +182,39 @@
 	return $cl;
 }
 
+__PACKAGE__->register_method(
+    api_name => "open-ils.circ.copy_location_order.update",
+    method => 'update_clo',
+    argc =>	2,
+);
 
+sub update_clo {
+    my($self, $client, $auth, $orders) = @_;
+    return [] unless $orders and @$orders;
 
+    my $e = new_editor(authtoken => $auth, xact =>1);
+    return $e->die_event unless $e->checkauth;
 
-23;
+    my $org = $$orders[0]->org;
+    return $e->die_event unless $e->allowed('ADMIN_COPY_LOCATION_ORDER', $org);
+
+    # clear out the previous order entries
+    my $existing = $e->search_asset_copy_location_order({org => $org});
+    $e->delete_asset_copy_location_order($_) or return $e->die_event for @$existing;
+
+    # create the new order entries
+    my $progress = 0; 
+    for my $order (@$orders) {
+        return $e->die_event(OpenILS::Event->new('BAD_PARAMS')) unless $order->org == $org;
+        $e->create_asset_copy_location_order($order) or return $e->die_event;
+        $client->respond({maximum => scalar(@$orders), progress => $progress}) unless ($progress++ % 10);
+    }
+
+    # fetch the new entries
+    $orders = $e->search_asset_copy_location_order({org => $org});
+    $e->commit;
+    return {orders => $orders};
+}
+
+
+1;

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -34,6 +34,8 @@
 use DateTime;
 use DateTime::Format::ISO8601;
 use OpenSRF::Utils qw/:datetime/;
+use Digest::MD5 qw(md5_hex);
+use OpenSRF::Utils::Cache;
 my $apputils = "OpenILS::Application::AppUtils";
 my $U = $apputils;
 
@@ -119,10 +121,10 @@
 
 sub create_hold {
 	my( $self, $conn, $auth, $hold ) = @_;
+    return -1 unless $hold;
 	my $e = new_editor(authtoken=>$auth, xact=>1);
-	return $e->event unless $e->checkauth;
+	return $e->die_event unless $e->checkauth;
 
-    return -1 unless $hold;
 	my $override = 1 if $self->api_name =~ /override/;
 
     my @events;
@@ -133,8 +135,8 @@
     if( $requestor->id ne $hold->usr ) {
         # Make sure the requestor is allowed to place holds for 
         # the recipient if they are not the same people
-        $recipient = $e->retrieve_actor_user($hold->usr)  or return $e->event;
-        $e->allowed('REQUEST_HOLDS', $recipient->home_ou) or return $e->event;
+        $recipient = $e->retrieve_actor_user($hold->usr)  or return $e->die_event;
+        $e->allowed('REQUEST_HOLDS', $recipient->home_ou) or return $e->die_event;
     }
 
     # If the related org setting tells us to, block if patron privs have expired
@@ -172,27 +174,30 @@
     push( @events, OpenILS::Event->new('HOLD_ITEM_CHECKED_OUT')) if $checked_out;
 
     if ( $t eq OILS_HOLD_TYPE_METARECORD ) {
-        return $e->event unless $e->allowed('MR_HOLDS',     $porg);
+        return $e->die_event unless $e->allowed('MR_HOLDS',     $porg);
     } elsif ( $t eq OILS_HOLD_TYPE_TITLE ) {
-        return $e->event unless $e->allowed('TITLE_HOLDS',  $porg);
+        return $e->die_event unless $e->allowed('TITLE_HOLDS',  $porg);
     } elsif ( $t eq OILS_HOLD_TYPE_VOLUME ) {
-        return $e->event unless $e->allowed('VOLUME_HOLDS', $porg);
+        return $e->die_event unless $e->allowed('VOLUME_HOLDS', $porg);
     } elsif ( $t eq OILS_HOLD_TYPE_ISSUANCE ) {
-        return $e->event unless $e->allowed('ISSUANCE_HOLDS', $porg);
+        return $e->die_event unless $e->allowed('ISSUANCE_HOLDS', $porg);
     } elsif ( $t eq OILS_HOLD_TYPE_COPY ) {
-        return $e->event unless $e->allowed('COPY_HOLDS',   $porg);
+        return $e->die_event unless $e->allowed('COPY_HOLDS',   $porg);
     } elsif ( $t eq OILS_HOLD_TYPE_FORCE ) {
-        return $e->event unless $e->allowed('COPY_HOLDS',   $porg);
+        return $e->die_event unless $e->allowed('COPY_HOLDS',   $porg);
     } elsif ( $t eq OILS_HOLD_TYPE_RECALL ) {
-        return $e->event unless $e->allowed('COPY_HOLDS',   $porg);
+        return $e->die_event unless $e->allowed('COPY_HOLDS',   $porg);
     }
 
     if( @events ) {
-        $override or return \@events;
+        if (!$override) {
+            $e->rollback;
+            return \@events;
+        }
         for my $evt (@events) {
             next unless $evt;
             my $name = $evt->{textcode};
-            return $e->event unless $e->allowed("$name.override", $porg);
+            return $e->die_event unless $e->allowed("$name.override", $porg);
         }
     }
 
@@ -208,7 +213,7 @@
     $hold->requestor($e->requestor->id); 
     $hold->request_lib($e->requestor->ws_ou);
     $hold->selection_ou($hold->pickup_lib) unless $hold->selection_ou;
-    $hold = $e->create_action_hold_request($hold) or return $e->event;
+    $hold = $e->create_action_hold_request($hold) or return $e->die_event;
 
 	$e->commit;
 
@@ -528,14 +533,20 @@
 sub uncancel_hold {
 	my($self, $client, $auth, $hold_id) = @_;
 	my $e = new_editor(authtoken=>$auth, xact=>1);
-	return $e->event unless $e->checkauth;
+	return $e->die_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 ($hold->fulfillment_time) {
+        $e->rollback;
+        return 0;
+    }
+    unless ($hold->cancel_time) {
+        $e->rollback;
+        return 1;
+    }
 
     # if configured to reset the request time, also reset the expire time
     if($U->ou_ancestor_setting_value(
@@ -583,22 +594,25 @@
 	my($self, $client, $auth, $holdid, $cause, $note) = @_;
 
 	my $e = new_editor(authtoken=>$auth, xact=>1);
-	return $e->event unless $e->checkauth;
+	return $e->die_event unless $e->checkauth;
 
 	my $hold = $e->retrieve_action_hold_request($holdid)
-		or return $e->event;
+		or return $e->die_event;
 
 	if( $e->requestor->id ne $hold->usr ) {
-		return $e->event unless $e->allowed('CANCEL_HOLDS');
+		return $e->die_event unless $e->allowed('CANCEL_HOLDS');
 	}
 
-	return 1 if $hold->cancel_time;
+	if ($hold->cancel_time) {
+        $e->rollback;
+        return 1;
+    }
 
 	# If the hold is captured, reset the copy status
 	if( $hold->capture_time and $hold->current_copy ) {
 
 		my $copy = $e->retrieve_asset_copy($hold->current_copy)
-			or return $e->event;
+			or return $e->die_event;
 
 		if( $copy->status == OILS_COPY_STATUS_ON_HOLDS_SHELF ) {
          $logger->info("canceling hold $holdid whose item is on the holds shelf");
@@ -630,11 +644,15 @@
     $hold->cancel_cause($cause);
     $hold->cancel_note($note);
 	$e->update_action_hold_request($hold)
-		or return $e->event;
+		or return $e->die_event;
 
 	delete_hold_copy_maps($self, $e, $hold->id);
 
 	$e->commit;
+
+    $U->create_events_for_hook('hold_request.cancel.staff', $hold, $hold->pickup_lib)
+        if $e->requestor->id != $hold->usr;
+
 	return 1;
 }
 
@@ -698,7 +716,10 @@
     my $e = new_editor(authtoken=>$auth, xact=>1);
     return $e->die_event unless $e->checkauth;
     my $resp = update_hold_impl($self, $e, $hold, $values);
-    return $resp if $U->event_code($resp);
+    if ($U->event_code($resp)) {
+        $e->rollback;
+        return $resp;
+    }
     $e->commit;     # FIXME: update_hold_impl already does $e->commit  ??
     return $resp;
 }
@@ -1033,11 +1054,7 @@
         # fetch cut_in_line and request_time since they're in the order_by
         # and we're asking for distinct values
         select => {ahr => ['id', 'cut_in_line', 'request_time']},
-        from   => {
-            ahr => {
-                ahcm => {type => 'left'} # there may be no copy maps 
-            }
-        },
+        from   => { ahr => 'ahcm' },
         order_by => [
             {
                 "class" => "ahr",
@@ -1049,29 +1066,41 @@
             { "class" => "ahr", "field" => "request_time" }
         ],
         distinct => 1,
-        where    => {
-            '-or' => [
+        where => {
+            '+ahcm' => {
+                target_copy => {
+                    in => {
+                        select => {ahcm => ['target_copy']},
+                        from   => 'ahcm',
+                        where  => {hold => $hold->id}
+                    } 
+                } 
+            }
+        }
+    });
+
+    if (!@$q_holds) { # none? maybe we don't have a map ... 
+        $q_holds = $e->json_query({
+            select => {ahr => ['id', 'cut_in_line', 'request_time']},
+            from   => 'ahr',
+            order_by => [
                 {
-                    '+ahcm' => {
-                        target_copy => {
-                            in => {
-                                select => {ahcm => ['target_copy']},
-                                from   => 'ahcm',
-                                where  => {hold => $hold->id}
-                            } 
-                        } 
-                    }
+                    "class" => "ahr",
+                    "field" => "cut_in_line",
+                    "transform" => "coalesce",
+                    "params" => [ 0 ],
+                    "direction" => "desc"
                 },
-                {
-                    '+ahr' => {
-                        hold_type => $hold->hold_type,
-                        target    => $hold->target
-                    }
-                }
-            ]
-        }, 
-    });
+                { "class" => "ahr", "field" => "request_time" }
+            ],
+            where    => {
+                hold_type => $hold->hold_type, 
+                target    => $hold->target 
+           } 
+        });
+    }
 
+
     my $qpos = 1;
     for my $h (@$q_holds) {
         last if $h->{id} == $hold->id;
@@ -1256,27 +1285,161 @@
 sub print_hold_pull_list {
     my($self, $client, $auth, $org_id) = @_;
 
-    my $e = new_editor(authtoken=>$auth, xact=>1);
-    return $e->die_event unless $e->checkauth;
+    my $e = new_editor(authtoken=>$auth);
+    return $e->event unless $e->checkauth;
 
     $org_id = (defined $org_id) ? $org_id : $e->requestor->ws_ou;
-    return $e->die_event unless $e->allowed('VIEW_HOLD', $org_id);
+    return $e->event unless $e->allowed('VIEW_HOLD', $org_id);
 
     my $hold_ids = $U->storagereq(
         'open-ils.storage.direct.action.hold_request.pull_list.id_list.current_copy_circ_lib.status_filtered.atomic',
         $org_id, 10000);
 
     return undef unless @$hold_ids;
+
     $client->status(new OpenSRF::DomainObject::oilsContinueStatus);
 
+    # Holds will /NOT/ be in order after this ...
     my $holds = $e->search_action_hold_request({id => $hold_ids}, {substream => 1});
     $client->status(new OpenSRF::DomainObject::oilsContinueStatus);
 
-    return $U->fire_object_event(undef, 'ahr.format.pull_list', $holds, $org_id);
+    # ... so we must resort.
+    my $hold_map = +{map { $_->id => $_ } @$holds};
+    my $sorted_holds = [];
+    push @$sorted_holds, $hold_map->{$_} foreach @$hold_ids;
+
+    return $U->fire_object_event(
+        undef, "ahr.format.pull_list", $sorted_holds,
+        $org_id, undef, undef, $client
+    );
+
 }
 
+__PACKAGE__->register_method(
+    method    => "print_hold_pull_list_stream",
+    stream   => 1,
+    api_name  => "open-ils.circ.hold_pull_list.print.stream",
+    signature => {
+        desc   => 'Returns a stream of fleshed holds',
+        params => [
+            { desc => 'Authtoken', type => 'string'},
+            { desc => 'Hash of optional param: Org unit ID (defaults to workstation org unit), limit, offset, sort (array of: acplo.position, call_number, request_time)',
+              type => 'object'
+            },
+        ],
+        return => {
+            desc => 'A stream of fleshed holds',
+            type => 'object'
+        }
+    }
+);
 
+sub print_hold_pull_list_stream {
+    my($self, $client, $auth, $params) = @_;
 
+    my $e = new_editor(authtoken=>$auth);
+    return $e->die_event unless $e->checkauth;
+
+    delete($$params{org_id}) unless (int($$params{org_id}));
+    delete($$params{limit}) unless (int($$params{limit}));
+    delete($$params{offset}) unless (int($$params{offset}));
+    delete($$params{chunk_size}) unless (int($$params{chunk_size}));
+    delete($$params{chunk_size}) if  ($$params{chunk_size} && $$params{chunk_size} > 50); # keep the size reasonable
+    $$params{chunk_size} ||= 10;
+
+    $$params{org_id} = (defined $$params{org_id}) ? $$params{org_id}: $e->requestor->ws_ou;
+    return $e->die_event unless $e->allowed('VIEW_HOLD', $$params{org_id });
+
+    my $sort = [];
+    if ($$params{sort} && @{ $$params{sort} }) {
+        for my $s (@{ $$params{sort} }) {
+            if ($s eq 'acplo.position') {
+                push @$sort, {
+                    "class" => "acplo", "field" => "position",
+                    "transform" => "coalesce", "params" => [999]
+                };
+            } elsif ($s eq 'call_number') {
+                push @$sort, {"class" => "acn", "field" => "label"};
+            } elsif ($s eq 'request_time') {
+                push @$sort, {"class" => "ahr", "field" => "request_time"};
+            }
+        }
+    } else {
+        push @$sort, {"class" => "ahr", "field" => "request_time"};
+    }
+
+    my $holds_ids = $e->json_query(
+        {
+            "select" => {"ahr" => ["id"]},
+            "from" => {
+                "ahr" => {
+                    "acp" => { 
+                        "field" => "id",
+                        "fkey" => "current_copy",
+                        "filter" => {
+                            "circ_lib" => $$params{org_id}, "status" => [0,7]
+                        },
+                        "join" => {
+                            "acn" => {
+                                "field" => "id",
+                                "fkey" => "call_number" 
+                            },
+                            "acplo" => {
+                                "field" => "org",
+                                "fkey" => "circ_lib", 
+                                "type" => "left",
+                                "filter" => {
+                                    "location" => {"=" => {"+acp" => "location"}}
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+            "where" => {
+                "+ahr" => {
+                    "capture_time" => undef,
+                    "cancel_time" => undef,
+                    "-or" => [
+                        {"expire_time" => undef },
+                        {"expire_time" => {">" => "now"}}
+                    ]
+                }
+            },
+            (@$sort ? (order_by => $sort) : ()),
+            ($$params{limit} ? (limit => $$params{limit}) : ()),
+            ($$params{offset} ? (offset => $$params{offset}) : ())
+        }, {"substream" => 1}
+    ) or return $e->die_event;
+
+    $logger->info("about to stream back " . scalar(@$holds_ids) . " holds");
+
+    my @chunk;
+    for my $hid (@$holds_ids) {
+        push @chunk, $e->retrieve_action_hold_request([
+            $hid->{"id"}, {
+                "flesh" => 3,
+                "flesh_fields" => {
+                    "ahr" => ["usr", "current_copy"],
+                    "au"  => ["card"],
+                    "acp" => ["location", "call_number"],
+                    "acn" => ["record"]
+                }
+            }
+        ]);
+
+        if (@chunk >= $$params{chunk_size}) {
+            $client->respond( \@chunk );
+            @chunk = ();
+        }
+    }
+    $client->respond_complete( \@chunk ) if (@chunk);
+    $e->disconnect;
+    return undef;
+}
+
+
+
 __PACKAGE__->register_method(
     method        => 'fetch_hold_notify',
     api_name      => 'open-ils.circ.hold_notification.retrieve_by_hold',
@@ -1332,7 +1495,7 @@
    return $e->die_event unless 
       $e->allowed('CREATE_HOLD_NOTIFICATION', $patron->home_ou);
 
-	$note->notify_staff($e->requestor->id);
+   $note->notify_staff($e->requestor->id);
    $e->create_action_hold_notification($note) or return $e->die_event;
    $e->commit;
    return $note->id;
@@ -1430,14 +1593,14 @@
 	if( $hold->capture_time and $hold->current_copy ) {
 
 		my $copy = $e->retrieve_asset_copy($hold->current_copy)
-			or return $e->event;
+			or return $e->die_event;
 
 		if( $copy->status == OILS_COPY_STATUS_ON_HOLDS_SHELF ) {
 			$logger->info("setting copy to status 'reshelving' on hold retarget");
 			$copy->status(OILS_COPY_STATUS_RESHELVING);
 			$copy->editor($e->requestor->id);
 			$copy->edit_date('now');
-			$e->update_asset_copy($copy) or return $e->event;
+			$e->update_asset_copy($copy) or return $e->die_event;
 
 		} elsif( $copy->status == OILS_COPY_STATUS_IN_TRANSIT ) {
 
@@ -1452,7 +1615,10 @@
 					$logger->info("Aborting transit [$transid] on hold [$hid] reset...");
 					my $evt = OpenILS::Application::Circ::Transit::__abort_transit($e, $trans, $copy, 1);
 					$logger->info("Transit abort completed with result $evt");
-					return $evt unless "$evt" eq 1;
+					unless ("$evt" eq 1) {
+                        $e->rollback;
+					    return $evt;
+                    }
 				}
 			}
 		}
@@ -1463,7 +1629,7 @@
 	$hold->clear_shelf_time;
 	$hold->clear_shelf_expire_time;
 
-	$e->update_action_hold_request($hold) or return $e->event;
+	$e->update_action_hold_request($hold) or return $e->die_event;
 	$e->commit;
 
 	$U->storagereq(
@@ -1585,8 +1751,8 @@
 	my( $self, $conn, $auth, $org ) = @_;
 
 	my $e = new_editor(authtoken => $auth);
-	return $e->event unless $e->checkauth;
-	return $e->event unless $e->allowed('VIEW_HOLD'); # XXX rely on editor perm
+	return $e->die_event unless $e->checkauth;
+	return $e->die_event unless $e->allowed('VIEW_HOLD'); # XXX rely on editor perm
 
 	$org ||= $e->requestor->ws_ou;
 
@@ -1639,6 +1805,84 @@
 }
 
 __PACKAGE__->register_method(
+    method    => "print_expired_holds_stream",
+    api_name  => "open-ils.circ.captured_holds.expired.print.stream",
+    stream    => 1
+);
+
+sub print_expired_holds_stream {
+    my ($self, $client, $auth, $params) = @_;
+
+    # No need to check specific permissions: we're going to call another method
+    # that will do that.
+    my $e = new_editor("authtoken" => $auth);
+    return $e->die_event unless $e->checkauth;
+
+    delete($$params{org_id}) unless (int($$params{org_id}));
+    delete($$params{limit}) unless (int($$params{limit}));
+    delete($$params{offset}) unless (int($$params{offset}));
+    delete($$params{chunk_size}) unless (int($$params{chunk_size}));
+    delete($$params{chunk_size}) if  ($$params{chunk_size} && $$params{chunk_size} > 50); # keep the size reasonable
+    $$params{chunk_size} ||= 10;
+
+    $$params{org_id} = (defined $$params{org_id}) ? $$params{org_id}: $e->requestor->ws_ou;
+
+    my @hold_ids = $self->method_lookup(
+        "open-ils.circ.captured_holds.id_list.expired_on_shelf.retrieve"
+    )->run($auth, $params->{"org_id"});
+
+    if (!@hold_ids) {
+        $e->disconnect;
+        return;
+    } elsif (defined $U->event_code($hold_ids[0])) {
+        $e->disconnect;
+        return $hold_ids[0];
+    }
+
+    $logger->info("about to stream back up to " . scalar(@hold_ids) . " expired holds");
+
+    while (@hold_ids) {
+        my @hid_chunk = splice @hold_ids, 0, $params->{"chunk_size"};
+
+        my $result_chunk = $e->json_query({
+            "select" => {
+                "acp" => ["barcode"],
+                "au" => [qw/
+                    first_given_name second_given_name family_name alias
+                /],
+                "acn" => ["label"],
+                "bre" => ["marc"],
+                "acpl" => ["name"]
+            },
+            "from" => {
+                "ahr" => {
+                    "acp" => {
+                        "field" => "id", "fkey" => "current_copy",
+                        "join" => {
+                            "acn" => {
+                                "field" => "id", "fkey" => "call_number",
+                                "join" => {
+                                    "bre" => {
+                                        "field" => "id", "fkey" => "record"
+                                    }
+                                }
+                            },
+                            "acpl" => {"field" => "id", "fkey" => "location"}
+                        }
+                    },
+                    "au" => {"field" => "id", "fkey" => "usr"}
+                }
+            },
+            "where" => {"+ahr" => {"id" => \@hid_chunk}}
+        }) or return $e->die_event;
+        $client->respond($result_chunk);
+    }
+
+    $e->disconnect;
+    undef;
+}
+
+__PACKAGE__->register_method(
     method    => "check_title_hold_batch",
     api_name  => "open-ils.circ.title_hold.is_possible.batch",
     stream    => 1,
@@ -2248,6 +2492,7 @@
 				copy				=> $copy,
 				pickup_lib			=> $hold->pickup_lib,
 				request_lib			=> $rlib,
+				retarget			=> 1
 			} 
 		);
 
@@ -2447,6 +2692,7 @@
         patron_first   => $user->first_given_name,
         patron_last    => $user->family_name,
         patron_barcode => $card->barcode,
+        patron_alias   => $user->alias,
         %$details
     };
 }
@@ -2510,7 +2756,87 @@
 	return ( $U->record_to_mvr($title), $volume, $copy, $issuance );
 }
 
+__PACKAGE__->register_method(
+    method    => 'clear_shelf_cache',
+    api_name  => 'open-ils.circ.hold.clear_shelf.get_cache',
+    stream    => 1,
+    signature => {
+        desc => q/
+            Returns the holds processed with the given cache key
+        /
+    }
+);
 
+sub clear_shelf_cache {
+    my($self, $client, $auth, $cache_key, $chunk_size) = @_;
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth and $e->allowed('VIEW_HOLD');
+
+    $chunk_size ||= 25;
+    my $hold_data = OpenSRF::Utils::Cache->new('global')->get_cache($cache_key);
+
+    if (!$hold_data) {
+        $logger->info("no hold data found in cache"); # XXX TODO return event
+        $e->rollback;
+        return undef;
+    }
+
+    my $maximum = 0;
+    foreach (keys %$hold_data) {
+        $maximum += scalar(@{ $hold_data->{$_} });
+    }
+    $client->respond({"maximum" => $maximum, "progress" => 0});
+
+    for my $action (sort keys %$hold_data) {
+        while (@{$hold_data->{$action}}) {
+            my @hid_chunk = splice @{$hold_data->{$action}}, 0, $chunk_size;
+
+            my $result_chunk = $e->json_query({
+                "select" => {
+                    "acp" => ["barcode"],
+                    "au" => [qw/
+                        first_given_name second_given_name family_name alias
+                    /],
+                    "acn" => ["label"],
+                    "bre" => ["marc"],
+                    "acpl" => ["name"],
+                    "ahr" => ["id"]
+                },
+                "from" => {
+                    "ahr" => {
+                        "acp" => {
+                            "field" => "id", "fkey" => "current_copy",
+                            "join" => {
+                                "acn" => {
+                                    "field" => "id", "fkey" => "call_number",
+                                    "join" => {
+                                        "bre" => {
+                                            "field" => "id", "fkey" => "record"
+                                        }
+                                    }
+                                },
+                                "acpl" => {"field" => "id", "fkey" => "location"}
+                            }
+                        },
+                        "au" => {"field" => "id", "fkey" => "usr"}
+                    }
+                },
+                "where" => {"+ahr" => {"id" => \@hid_chunk}}
+            }, {"substream" => 1}) or return $e->die_event;
+
+            $client->respond([
+                map {
+                    +{"action" => $action, "hold_details" => $_}
+                } @$result_chunk
+            ]);
+        }
+    }
+
+    $e->rollback;
+    return undef;
+}
+
+
 __PACKAGE__->register_method(
     method    => 'clear_shelf_process',
     stream    => 1,
@@ -2532,6 +2858,7 @@
 
 	my $e = new_editor(authtoken=>$auth, xact => 1);
 	$e->checkauth or return $e->die_event;
+	my $cache = OpenSRF::Utils::Cache->new('global');
 
     $org_id ||= $e->requestor->ws_ou;
 	$e->allowed('UPDATE_HOLD', $org_id) or return $e->die_event;
@@ -2544,13 +2871,16 @@
             pickup_lib        => $org_id,
             cancel_time       => undef,
             fulfillment_time  => undef,
-            shelf_time        => {'!=' => undef}
+            shelf_time        => {'!=' => undef},
+            capture_time      => {'!=' => undef},
+            current_copy      => {'!=' => undef},
         },
         { idlist => 1 }
     );
 
-
     my @holds;
+    my $chunk_size = 25; # chunked status updates
+    my $counter = 0;
     for my $hold_id (@$hold_ids) {
 
         $logger->info("Clear shelf processing hold $hold_id");
@@ -2577,52 +2907,48 @@
         }
 
         push(@holds, $hold);
+        $client->respond({maximum => scalar(@holds), progress => $counter}) if ( (++$counter % $chunk_size) == 0);
     }
 
     if ($e->commit) {
 
+        my %cache_data = (
+            hold => [],
+            transit => [],
+            shelf => []
+        );
+
         for my $hold (@holds) {
 
             my $copy = $hold->current_copy;
-
             my ($alt_hold) = __PACKAGE__->find_nearest_permitted_hold($e, $copy, $e->requestor, 1);
 
             if($alt_hold) {
 
-                # copy is needed for a hold
-                $client->respond({action => 'hold', copy => $copy, hold_id => $hold->id});
+                push(@{$cache_data{hold}}, $hold->id); # copy is needed for a hold
 
             } elsif($copy->circ_lib != $e->requestor->ws_ou) {
 
-                # copy needs to transit
-                $client->respond({action => 'transit', copy => $copy, hold_id => $hold->id});
+                push(@{$cache_data{transit}}, $hold->id); # copy needs to transit
 
             } else {
 
-                # copy needs to go back to the shelf
-                $client->respond({action => 'shelf', copy => $copy, hold_id => $hold->id});
+                push(@{$cache_data{shelf}}, $hold->id); # copy needs to go back to the shelf
             }
         }
 
+        my $cache_key = md5_hex(time . $$ . rand());
+        $logger->info("clear_shelf_cache: storing under $cache_key");
+        $cache->put_cache($cache_key, \%cache_data, 7200); # TODO: 2 hours.  configurable?
+
         # tell the client we're done
-        $client->respond_complete;
+        $client->respond_complete({cache_key => $cache_key});
 
-        # fire off the hold cancelation trigger
-        my $trigger = OpenSRF::AppSession->connect('open-ils.trigger');
+        # fire off the hold cancelation trigger and wait for response so don't flood the service
+        $U->create_events_for_hook(
+            'hold_request.cancel.expire_holds_shelf', 
+            $_, $org_id, undef, undef, 1) for @holds;
 
-        for my $hold (@holds) {
-
-            my $req = $trigger->request(
-                'open-ils.trigger.event.autocreate', 
-                'hold_request.cancel.expire_holds_shelf', 
-                $hold, $org_id);
-
-            # wait for response so don't flood the service
-            $req->recv;
-        }
-
-        $trigger->disconnect;
-
     } else {
         # tell the client we're done
         $client->respond_complete;
@@ -2858,7 +3184,7 @@
     my( $self, $client, $auth, $new_bib_id, $bib_ids ) = @_;
 
     my $e = new_editor(authtoken=>$auth, xact=>1);
-    return $e->event unless $e->checkauth;
+    return $e->die_event unless $e->checkauth;
 
     my $holds = $e->search_action_hold_request(
         [

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1487,7 +1487,7 @@
 sub fire_circ_events {
     my($self, $conn, $auth, $org_id, $event_def, $hook, $granularity, $target_ids, $user_data) = @_;
 
-    my $e = new_editor(authtoken => $auth);
+    my $e = new_editor(authtoken => $auth, xact => 1);
 	return $e->event unless $e->checkauth;
 
     my $targets;
@@ -1502,6 +1502,10 @@
         return $e->event unless $e->allowed('VIEW_CIRCULATIONS', $org_id);
         $targets = $e->batch_retrieve_action_circulation($target_ids);
     }
+    $e->rollback; # FIXME using transaction because of pgpool/slony setups, but not
+                  # simply making this method authoritative because of weirdness
+                  # with transaction handling in A/T code that causes rollback
+                  # failure down the line if handling many targets
 
     return undef unless @$targets;
     return $U->fire_object_event($event_def, $hook, $targets, $org_id, $granularity, $user_data);

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Search/Biblio.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Search/Biblio.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Search/Biblio.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1108,6 +1108,7 @@
     my $search_duration;
     my $user_offset = $search_hash->{offset} ||  0; # user-specified offset
     my $user_limit  = $search_hash->{limit}  || 10;
+    my $ignore_facet_classes  = $search_hash->{ignore_facet_classes};
     $user_offset = ($user_offset >= 0) ? $user_offset :  0;
     $user_limit  = ($user_limit  >= 0) ? $user_limit  : 10;
 
@@ -1262,7 +1263,7 @@
         }
     );
 
-    cache_facets($facet_key, $new_ids, $IAmMetabib) if $docache;
+    cache_facets($facet_key, $new_ids, $IAmMetabib, $ignore_facet_classes) if $docache;
 
     return undef;
 }
@@ -1368,10 +1369,14 @@
 
 sub cache_facets {
     # add facets for this search to the facet cache
-    my($key, $results, $metabib) = @_;
+    my($key, $results, $metabib, $ignore) = @_;
     my $data = $cache->get_cache($key);
     $data ||= {};
 
+    if (!ref($ignore)) {
+        $ignore = ['identifier']; # ignore the identifier class by default
+    }
+
     return undef unless (@$results);
 
     # The query we're constructing
@@ -1398,11 +1403,13 @@
             },
             from    => {
                 mfae => {
-                    mmrsm => { field => 'source', fkey => 'source' }
+                    mmrsm => { field => 'source', fkey => 'source' },
+                    cmf   => { field => 'id', fkey => 'field' }
                 }
             },
             where   => {
-                '+mmrsm' => { $count_field => $results }
+                '+mmrsm' => { $count_field => $results },
+                '+cmf'   => { field_class => { 'not in' => $ignore } }
             }
         }
     );

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/asset.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/asset.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/asset.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -10,7 +10,7 @@
 
 __PACKAGE__->table( 'asset_copy_location' );
 __PACKAGE__->columns( Primary => qw/id/ );
-__PACKAGE__->columns( Essential => qw/name owning_lib holdable hold_verify opac_visible circulate/ );
+__PACKAGE__->columns( Essential => qw/name owning_lib holdable hold_verify opac_visible circulate label_prefix label_suffix/ );
 
 #-------------------------------------------------------------------------------
 package asset::copy_location_order;
@@ -35,7 +35,7 @@
 __PACKAGE__->table( 'asset_call_number' );
 __PACKAGE__->columns( Primary => qw/id/ );
 __PACKAGE__->columns( Essential => qw/record label creator create_date editor
-				   edit_date record label owning_lib deleted/ );
+				   edit_date record label owning_lib deleted label_class label_sortkey/ );
 
 #-------------------------------------------------------------------------------
 package asset::call_number_note;
@@ -56,7 +56,7 @@
 				   fine_level circulate deposit price ref opac_visible
 				   circ_as_type circ_modifier deposit_amount location mint_condition
 				   holdable dummy_title dummy_author deleted alert_message
-				   age_protect floating/ );
+				   age_protect floating cost status_changed_time/ );
 
 #-------------------------------------------------------------------------------
 package asset::stat_cat;

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/permission.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/permission.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/permission.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -16,7 +16,7 @@
 __PACKAGE__->table('permission_grp_tree');
 __PACKAGE__->columns(Primary => qw/id/);
 __PACKAGE__->columns(Essential => qw/name parent description perm_interval
-				     application_perm usergroup/);
+				     application_perm usergroup hold_priority/);
 #-------------------------------------------------------------------------------
 package permission::usr_grp_map;
 use base qw/permission/;

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/serial.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/serial.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/serial.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -46,5 +46,16 @@
 				   holdable dummy_title dummy_author deleted alert_message label
 				   age_protect floating label_sort_key contents/ );
 
+#-------------------------------------------------------------------------------
+package serial::record_entry;
+use base qw/serial/;
+
+__PACKAGE__->table( 'serial_record_entry' );
+__PACKAGE__->columns( Primary => qw/id/ );
+__PACKAGE__->columns( Essential => qw/active record create_date creator
+                        deleted edit_date editor id last_xact_id marc source
+                        owning_lib/ );
+
+
 1;
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -419,11 +419,11 @@
     }
 
     if (($filters{preferred_language} || $self->QueryParser->default_preferred_language) && ($filters{preferred_language_multiplier} || $self->QueryParser->default_preferred_language_multiplier)) {
-        $rel = "($rel * CASE WHEN FIRST(mrd.item_lang) = ". $self->QueryParser->quote_value( $filters{preferred_language} ? $filters{preferred_language} : $self->QueryParser->default_preferred_language ) . " THEN ";
-        $rel .= $filters{preferred_language_multiplier} ? $filters{preferred_language_multiplier} : $self->QueryParser->default_preferred_language_multiplier;
-        $rel .= " ELSE 1 END)";
+        my $pl = $self->QueryParser->quote_value( $filters{preferred_language} ? $filters{preferred_language} : $self->QueryParser->default_preferred_language );
+        my $plw = $filters{preferred_language_multiplier} ? $filters{preferred_language_multiplier} : $self->QueryParser->default_preferred_language_multiplier;
+        $rel = "($rel * COALESCE( NULLIF( FIRST(mrd.item_lang) = $pl , FALSE )::INT * $plw, 1))";
     }
-    $rel .= "::NUMERIC";
+    $rel .= '::NUMERIC';
 
     for my $f ( qw/audience vr_format item_type item_form lit_form language bib_level/ ) {
         my $col = $f;
@@ -449,44 +449,22 @@
     $desc = 'DESC' if ($self->find_modifier('descending'));
 
     if ($sort_filter eq 'rel') { # relevance ranking flips sort dir
-         if ($desc eq  'ASC') {
+        if ($desc eq  'ASC') {
             $desc = 'DESC';
         } else {
             $desc = 'ASC';
         }
     } else {
         if ($sort_filter eq 'title') {
-            my $default = $desc eq 'DESC' ? '       ' : 'zzzzzz';
-            $rank = <<"            SQL";
-( COALESCE( FIRST ((
-                SELECT  frt.value
-                  FROM  metabib.full_rec frt
-                  WHERE frt.record = m.source
-                    AND frt.tag = 'tnf'
-                    AND frt.subfield = 'a'
-                  LIMIT 1
-        )),'$default'))::TEXT
-            SQL
+            $rank = "FIRST((SELECT frt.value FROM metabib.full_rec frt WHERE frt.record = m.source AND frt.tag = 'tnf' AND frt.subfield = 'a' LIMIT 1))";
         } elsif ($sort_filter eq 'pubdate') {
-            my $default = $desc eq 'DESC' ? '0' : '99999';
-            $rank = "COALESCE( FIRST(NULLIF(LPAD(REGEXP_REPLACE(mrd.date1, E'\\\\D+', '0', 'g'),4,'0'),'0000')), '$default' )::INT";
+            $rank = "FIRST(mrd.date1)::NUMERIC";
         } elsif ($sort_filter eq 'create_date') {
-            $rank = "( FIRST (( SELECT create_date FROM biblio.record_entry rbr WHERE rbr.id = m.source)) )::TIMESTAMPTZ";
+            $rank = "FIRST((SELECT create_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))";
         } elsif ($sort_filter eq 'edit_date') {
-            $rank = "( FIRST (( SELECT edit_date FROM biblio.record_entry rbr WHERE rbr.id = m.source)) )::TIMESTAMPTZ";
+            $rank = "FIRST((SELECT edit_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))";
         } elsif ($sort_filter eq 'author') {
-            my $default = $desc eq 'DESC' ? '       ' : 'zzzzzz';
-            $rank = <<"            SQL"
-( COALESCE( FIRST ((
-                SELECT  LTRIM(fra.value)
-                  FROM  metabib.full_rec fra
-                  WHERE fra.record = m.source
-                    AND fra.tag LIKE '1%'
-                    AND fra.subfield = 'a'
-                  ORDER BY fra.tag::text::int
-                  LIMIT 1
-        )),'$default'))::TEXT
-            SQL
+            $rank = "FIRST((SELECT fra.value FROM metabib.full_rec fra WHERE fra.record = m.source AND fra.tag LIKE '1%' AND fra.subfield = 'a' ORDER BY fra.tag LIMIT 1))";
         } else {
             # default to rel ranking
             $rank = $rel;
@@ -532,7 +510,7 @@
         ARRAY_ACCUM(DISTINCT m.source) AS records,
         $rel AS rel,
         $rank AS rank, 
-        COALESCE( FIRST(NULLIF(LPAD(REGEXP_REPLACE(mrd.date1, E'\\\\D+', '0', 'g'),4,'0'),'0000')), '0' )::INT AS tie_break
+        FIRST(mrd.date1) AS tie_break
   FROM  metabib.metarecord_source_map m
         JOIN metabib.rec_descriptor mrd ON (m.source = mrd.record)
         $$flat_plan{from}
@@ -550,7 +528,7 @@
         $bib_level
         AND $$flat_plan{where}
   GROUP BY 1
-  ORDER BY 4 $desc, 5 DESC, 3 DESC
+  ORDER BY 4 $desc NULLS LAST, 5 DESC NULLS LAST, 3 DESC
   LIMIT $core_limit
 SQL
 
@@ -570,13 +548,13 @@
     return '' if (!@$only_atoms);
 
     if ($bump eq 'first_word') {
-        return " /* first_word */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ ('^'||naco_normalize(".$self->QueryParser->quote_value($only_atoms->[0]->content).")))::BOOL::INT, 0 ) * $multiplier, 1)";
+        return " /* first_word */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ ('^'||naco_normalize(".$self->QueryParser->quote_value($only_atoms->[0]->content)."))), FALSE )::INT * $multiplier, 1)";
     } elsif ($bump eq 'full_match') {
         return " /* full_match */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ ('^'||".
-                    join( "||' '||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms )."||'\$'))::BOOL::INT, 0 ) * $multiplier, 1)";
+                    join( "||' '||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms )."||'\$')), FALSE )::INT * $multiplier, 1)";
     } elsif ($bump eq 'word_order') {
         return " /* word_order */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ (".
-                    join( "||'.*'||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms )."))::BOOL::INT, 0 ) * $multiplier, 1)";
+                    join( "||'.*'||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms ).")), FALSE )::INT * $multiplier, 1)";
     }
 
     return '';
@@ -605,21 +583,24 @@
                 my $node_rank = $node->rank . " * ${talias}.weight";
 
                 my $core_limit = $self->QueryParser->core_limit || 25000;
-                $from .= "\n\tLEFT JOIN (\n\t\tSELECT fe.*, fe_weight.weight /* search */\n\t\t  FROM  $table AS fe";
+                $from .= "\n\tLEFT JOIN (\n\t\tSELECT fe.*, fe_weight.weight, x.tsq /* search */\n\t\t  FROM  $table AS fe";
                 $from .= "\n\t\t\tJOIN config.metabib_field AS fe_weight ON (fe_weight.id = fe.field)";
-                $from .= "\n\t\t  WHERE fe.index_vector @@ (" .$node->tsquery . ')';
+                $from .= "\n\t\t\tJOIN (SELECT ".$node->tsquery ." AS tsq ) AS x ON (fe.index_vector @@ x.tsq)";
 
                 my @bump_fields;
                 if (@{$node->fields} > 0) {
                     @bump_fields = @{$node->fields};
-                    $from .= "\n\t\t\tAND fe_weight.field_class = ". $self->QueryParser->quote_value($node->classname) ." AND fe_weight.name IN (";
-                    $from .= join(",", map { $self->QueryParser->quote_value($_) } @{$node->fields}) . ")";
 
+                    my @field_ids;
+                    push(@field_ids, $self->QueryParser->search_field_ids_by_class( $node->classname, $_ )->[0]) for (@bump_fields);
+                    $from .= "\n\t\t\tWHERE fe_weight.id IN  (". join(',', @field_ids) .")";
+
                 } else {
                     @bump_fields = @{$self->QueryParser->search_fields->{$node->classname}};
                 }
 
-                $from .= "\n\t\tLIMIT $core_limit\n\t) AS $talias ON (m.source = $talias.source)";
+                ###$from .= "\n\t\tLIMIT $core_limit";
+                $from .= "\n\t) AS $talias ON (m.source = ${talias}.source)";
 
 
                 my %used_bumps;
@@ -638,7 +619,7 @@
                 }
 
                 $where .= '(' . $talias . ".id IS NOT NULL";
-                $where .= ' AND ' . join(' AND ', map {"$talias.value ~* ".$self->QueryParser->quote_value($_)} @{$node->phrases}) if (@{$node->phrases});
+                $where .= ' AND ' . join(' AND ', map {"${talias}.value ~* ".$self->QueryParser->quote_value($_)} @{$node->phrases}) if (@{$node->phrases});
                 $where .= ')';
 
                 push @rank_list, $node_rank;
@@ -648,19 +629,17 @@
                 my $table = $node->table;
                 my $talias = $node->table_alias;
 
-                $from .= "\n\tJOIN (\n\t\tSELECT * /* facet */\n\t\t  FROM metabib.facet_entry\n\t\t  WHERE ".
-                         "SUBSTRING(value,1,1024) IN (" . join(",", map { $self->QueryParser->quote_value($_) } @{$node->values}) . ")".
-                         "\n\t\t\tAND field IN (SELECT id FROM config.metabib_field WHERE field_class = ". $self->QueryParser->quote_value($node->classname) ." AND facet_field";
-
+                my @field_ids;
                 if (@{$node->fields} > 0) {
-                    $from .= " AND name IN (";
-                    $from .= join(",", map { $self->QueryParser->quote_value($_) } @{$node->fields}) . ")";
+                    push(@field_ids, $self->QueryParser->facet_field_ids_by_class( $node->classname, $_ )->[0]) for (@{$node->fields});
+                } else {
+                    @field_ids = @{ $self->QueryParser->facet_field_ids_by_class( $node->classname ) };
                 }
 
-                $from .= ")";
+                $from .= "\n\tJOIN /* facet */ metabib.facet_entry $talias ON (\n\t\tm.source = ${talias}.source\n\t\t".
+                         "AND SUBSTRING(${talias}.value,1,1024) IN (" . join(",", map { $self->QueryParser->quote_value($_) } @{$node->values}) . ")\n\t\t".
+                         "AND ${talias}.field IN (". join(',', @field_ids) . ")\n\t)";
 
-                $from .= "\n\t\t) AS $talias ON (m.source = $talias.source)";
-
                 $where .= 'TRUE';
 
             } else {
@@ -836,7 +815,7 @@
 sub rank {
     my $self = shift;
     return $self->{rank} if ($self->{rank});
-    return $self->{rank} = 'rank(' . $self->table_alias . '.index_vector, ' . $self->tsquery . ')';
+    return $self->{rank} = 'rank(' . $self->table_alias . '.index_vector, ' . $self->table_alias . '.tsq)';
 }
 
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -286,14 +286,16 @@
 	my $fifo = shift();
 
 	my $holdsort = isTrue($fifo) ?
-            "CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.request_time, h.selection_depth DESC, p.prox " :
-            "p.prox, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.selection_depth DESC, h.request_time ";
+            "pgt.hold_priority, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.request_time, h.selection_depth DESC, p.prox " :
+            "p.prox, pgt.hold_priority, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.selection_depth DESC, h.request_time ";
 
 	my $ids = action::hold_request->db_Main->selectcol_arrayref(<<"	SQL", {}, $here, $cp, $age);
 		SELECT	h.id
 		  FROM	action.hold_request h
 			JOIN actor.org_unit_proximity p ON (p.from_org = ? AND p.to_org = h.pickup_lib)
 		  	JOIN action.hold_copy_map hm ON (hm.hold = h.id)
+		  	JOIN actor.usr au ON (au.id = h.usr)
+		  	JOIN permission.grp_tree pgt ON (au.profile = pgt.id)
 		  WHERE hm.target_copy = ?
 		  	AND (AGE(NOW(),h.request_time) >= CAST(? AS INTERVAL) OR p.prox = 0)
 			AND h.capture_time IS NULL
@@ -1271,11 +1273,12 @@
 			$$prox_list[0] =
 			[
 				grep {
-					''.$_->circ_lib eq $pu_lib
+					''.$_->circ_lib eq $pu_lib &&
+                    ( $_->status == 0 || $_->status == 7 )
 				} @good_copies
 			];
 
-			$all_copies = [grep {''.$_->circ_lib ne $pu_lib } @good_copies];
+			$all_copies = [grep { $_->status == 0 || $_->status == 7 } grep {''.$_->circ_lib ne $pu_lib } @good_copies];
 			# $all_copies is now a list of copies not at the pickup library
 
 			my $best = choose_nearest_copy($hold, $prox_list);
@@ -1294,7 +1297,7 @@
 					my %circ_lib_map =  map { (''.$_->circ_lib => 1) } @$all_copies;
 					my $circ_lib_list = [keys %circ_lib_map];
 	
-					my $cstore = OpenSRF::AppSession->connect('open-ils.cstore');
+					my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
 	
 					# Grab the "biggest" loop for this hold so far
 					my $current_loop = $cstore->request(
@@ -1639,163 +1642,7 @@
 my $locations;
 my $statuses;
 my %cache = (titles => {}, cns => {});
-sub hold_copy_targeter {
-	my $self = shift;
-	my $client = shift;
-	my $check_expire = shift;
-	my $one_hold = shift;
 
-	$self->{user_filter} = OpenSRF::AppSession->create('open-ils.circ');
-	$self->{user_filter}->connect;
-	$self->{client} = $client;
-
-	my $time = time;
-	$check_expire ||= '12h';
-	$check_expire = interval_to_seconds( $check_expire );
-
-	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time() - $check_expire);
-	$year += 1900;
-	$mon += 1;
-	my $expire_threshold = sprintf(
-		'%s-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d-00',
-		$year, $mon, $mday, $hour, $min, $sec
-	);
-
-
-	$statuses ||= [ config::copy_status->search(holdable => 't') ];
-
-	$locations ||= [ asset::copy_location->search(holdable => 't') ];
-
-	my $holds;
-
-	%cache = (titles => {}, cns => {});
-
-	try {
-		if ($one_hold) {
-			$holds = [ action::hold_request->search(id => $one_hold) ];
-		} else {
-			$holds = [ action::hold_request->search_where(
-							{ capture_time => undef,
-							  prev_check_time => { '<=' => $expire_threshold },
-							},
-							{ order_by => 'request_time,prev_check_time' } ) ];
-			push @$holds, action::hold_request->search_where(
-							{ capture_time => undef,
-				  			  prev_check_time => undef,
-							},
-							{ order_by => 'request_time' } );
-		}
-	} catch Error with {
-		my $e = shift;
-		die "Could not retrieve uncaptured hold requests:\n\n$e\n";
-	};
-
-	for my $hold (@$holds) {
-		try {
-			#action::hold_request->db_Main->begin_work;
-			if ($self->method_lookup('open-ils.storage.transaction.current')->run) {
-				$client->respond("Cleaning up after previous transaction\n");
-				$self->method_lookup('open-ils.storage.transaction.rollback')->run;
-			}
-			$self->method_lookup('open-ils.storage.transaction.begin')->run( $client );
-			$client->respond("Processing hold ".$hold->id."...\n");
-
-			my $copies;
-
-			$copies = $self->metarecord_hold_capture($hold) if ($hold->hold_type eq 'M');
-			$self->{client}->status( new OpenSRF::DomainObject::oilsContinueStatus );
-
-			$copies = $self->title_hold_capture($hold) if ($hold->hold_type eq 'T');
-			$self->{client}->status( new OpenSRF::DomainObject::oilsContinueStatus );
-			
-			$copies = $self->volume_hold_capture($hold) if ($hold->hold_type eq 'V');
-			$self->{client}->status( new OpenSRF::DomainObject::oilsContinueStatus );
-			
-			$copies = $self->copy_hold_capture($hold) if ($hold->hold_type eq 'C');
-
-			unless (ref $copies || !@$copies) {
-				$client->respond("\tNo copies available for targeting at all!\n");
-			}
-
-			my @good_copies;
-			for my $c (@$copies) {
-				next if ( grep {$c->id == $hold->current_copy} @good_copies);
-				push @good_copies, $c if ($c);
-			}
-
-			$client->respond("\t".scalar(@good_copies)." (non-current) copies available for targeting...\n");
-
-			my $old_best = $hold->current_copy;
-			$hold->update({ current_copy => undef });
-	
-			if (!scalar(@good_copies)) {
-				$client->respond("\tNo (non-current) copies available to fill the hold.\n");
-				if ( $old_best && grep {$_->id == $hold->current_copy} @$copies ) {
-					$client->respond("\tPushing current_copy back onto the targeting list\n");
-					push @good_copies, asset::copy->retrieve( $old_best );
-				} else {
-					$client->respond("\tcurrent_copy is no longer available for targeting... NEXT HOLD, PLEASE!\n");
-					next;
-				}
-			}
-
-			my $prox_list;
-			$$prox_list[0] = [grep {$_->circ_lib == $hold->pickup_lib } @good_copies];
-			$copies = [grep {$_->circ_lib != $hold->pickup_lib } @good_copies];
-
-			my $best = choose_nearest_copy($hold, $prox_list);
-
-			if (!$best) {
-				$prox_list = create_prox_list( $self, $hold->pickup_lib, $copies );
-				$best = choose_nearest_copy($hold, $prox_list);
-			}
-
-			if ($old_best) {
-				# hold wasn't fulfilled, record the fact
-			
-				$client->respond("\tHold was not (but should have been) fulfilled by ".$old_best->id.".\n");
-				action::unfulfilled_hold_list->create(
-						{ hold => ''.$hold->id,
-						  current_copy => ''.$old_best->id,
-						  circ_lib => ''.$old_best->circ_lib,
-						});
-			}
-
-			if ($best) {
-				$hold->update( { current_copy => ''.$best->id } );
-				$client->respond("\tTargeting copy ".$best->id." for hold fulfillment.\n");
-			}
-
-			$hold->update( { prev_check_time => 'now' } );
-			$client->respond("\tUpdating hold ".$hold->id." with new 'current_copy' for hold fulfillment.\n");
-
-			$client->respond("\tProcessing of hold ".$hold->id." complete.\n");
-			$self->method_lookup('open-ils.storage.transaction.commit')->run;
-
-			#action::hold_request->dbi_commit;
-
-		} otherwise {
-			my $e = shift;
-			$log->error("Processing of hold failed:  $e");
-			$client->respond("\tProcessing of hold failed!.\n\t\t$e\n");
-			$self->method_lookup('open-ils.storage.transaction.rollback')->run;
-			#action::hold_request->dbi_rollback;
-		};
-	}
-
-	$self->{user_filter}->disconnect;
-	$self->{user_filter}->finish;
-	delete $$self{user_filter};
-	return undef;
-}
-#__PACKAGE__->register_method(
-#	api_name        => 'open-ils.storage.action.hold_request.copy_targeter',
-#	api_level       => 0,
-#	stream		=> 1,
-#	method          => 'hold_copy_targeter',
-#);
-
-
 sub copy_hold_capture {
 	my $self = shift;
 	my $hold = shift;
@@ -1864,21 +1711,24 @@
 	for my $p ( 0 .. int( scalar(@$prox_list) - 1) ) {
 		next unless (ref $$prox_list[$p]);
 
-		my @capturable = grep { $_->status == 0 || $_->status == 7 } @{ $$prox_list[$p] };
+		my @capturable = @{ $$prox_list[$p] };
 		next unless (@capturable);
 
 		my $rand = int(rand(scalar(@capturable)));
-		while (my ($c) = splice(@capturable,$rand)) {
-			return $c if ( OpenILS::Utils::PermitHold::permit_copy_hold(
+		my %seen = ();
+		while (my ($c) = splice(@capturable, $rand, 1)) {
+			return $c if !exists($seen{$c->id}) && ( OpenILS::Utils::PermitHold::permit_copy_hold(
 				{ title => $c->call_number->record->to_fieldmapper,
 				  title_descriptor => $c->call_number->record->record_descriptor->next->to_fieldmapper,
 				  patron => $hold->usr->to_fieldmapper,
 				  copy => $c->to_fieldmapper,
 				  requestor => $hold->requestor->to_fieldmapper,
 				  request_lib => $hold->request_lib->to_fieldmapper,
-				   pickup_lib => $hold->pickup_lib->id,
+				  pickup_lib => $hold->pickup_lib->id,
+				  retarget => 1
 				}
 			));
+			$seen{$c->id}++;
 
 			last unless(@capturable);
 			$rand = int(rand(scalar(@capturable)));
@@ -1898,15 +1748,16 @@
 		my ($prox) = $self->method_lookup('open-ils.storage.asset.copy.proximity')->run( $cp, $lib );
 		next unless (defined($prox));
 
+        my $copy_circ_lib = ''.$cp->circ_lib;
 		# Fetch the weighting value for hold targeting, defaulting to 1
-		$self->{target_weight}{$lib} ||= $actor->request(
-			'open-ils.actor.ou_setting.ancestor_default' => $lib.'' => 'circ.holds.org_unit_target_weight'
+		$self->{target_weight}{$copy_circ_lib} ||= $actor->request(
+			'open-ils.actor.ou_setting.ancestor_default' => $copy_circ_lib.'' => 'circ.holds.org_unit_target_weight'
 		)->gather(1);
-        $self->{target_weight}{$lib} = $self->{target_weight}{$lib}{value} if (ref $self->{target_weight}{$lib});
-        $self->{target_weight}{$lib} ||= 1;
+        $self->{target_weight}{$copy_circ_lib} = $self->{target_weight}{$copy_circ_lib}{value} if (ref $self->{target_weight}{$copy_circ_lib});
+        $self->{target_weight}{$copy_circ_lib} ||= 1;
 
 		$prox_list[$prox] = [] unless defined($prox_list[$prox]);
-		for my $w ( 1 .. $self->{target_weight}{$lib} ) {
+		for my $w ( 1 .. $self->{target_weight}{$copy_circ_lib} ) {
 			push @{$prox_list[$prox]}, $cp;
 		}
 	}

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/asset.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/asset.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/asset.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -230,9 +230,9 @@
 		        $table cn
 		where
 			not deleted
-		        and (upper(label) > ? or ( cn.id > ? and upper(label) = ? ))
+		        and (oils_text_as_bytea(upper(label)) > ? or ( cn.id > ? and oils_text_as_bytea(upper(label)) = ? ))
 			and owning_lib in ($orgs)
-		order by upper(label), 4, 2
+		order by oils_text_as_bytea(upper(label)), 4, 2
 		limit $size;
 	SQL
 
@@ -285,9 +285,9 @@
 			        $table cn
 			where
 				not deleted
-			        and (upper(label) < ? or ( cn.id < ? and upper(label) = ? ))
+			        and (oils_text_as_bytea(upper(label)) < ? or ( cn.id < ? and oils_text_as_bytea(upper(label)) = ? ))
 				and owning_lib in ($orgs)
-			order by upper(label) desc, 4 desc, 2 desc
+			order by oils_text_as_bytea(upper(label)) desc, 4 desc, 2 desc
 			limit $size
 		) as bar
 		order by 1,4,2;
@@ -343,9 +343,9 @@
 			        $table cn
 			where
 				not deleted
-			        and upper(label) < ?
+			        and oils_text_as_bytea(upper(label)) < ?
 				and owning_lib in ($orgs)
-			order by upper(label) desc, 4 desc, 2 desc
+			order by oils_text_as_bytea(upper(label)) desc, 4 desc, 2 desc
 			limit $topsize
 		) as bar
 		order by 1,4,2;
@@ -361,9 +361,9 @@
 		        $table cn
 		where
 			not deleted
-		        and upper(label) >= ?
+		        and oils_text_as_bytea(upper(label)) >= ?
 			and owning_lib in ($orgs)
-		order by upper(label),4,2
+		order by oils_text_as_bytea(upper(label)),4,2
 		limit $bottomsize;
 	SQL
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/authority.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/authority.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/authority.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -33,12 +33,8 @@
 	for my $t ( @tags ) {
 		for my $search ( @searches ) {
 			my $sf = $$search{subfield};
-			my $term = NFD(lc($$search{term}));
+			my $term = OpenILS::Application::Storage::FTS::naco_normalize($$search{term}, $sf);
 
-			$term =~ s/\pM+//sgo;
-			$term =~ s/\pC+//sgo;
-			$term =~ s/\W+$//o;
-
 			$tag = [$tag] if (!ref($tag));
 
 			push @values, $t, $sf, $term;

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -57,7 +57,7 @@
 			item_type,
 			item_form,
 			quality,
-			FIRST(COALESCE(LTRIM(SUBSTR( value, COALESCE(SUBSTRING(ind2 FROM '\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')) AS title
+			FIRST(COALESCE(LTRIM(SUBSTR( value, COALESCE(SUBSTRING(ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')) AS title
 		FROM	(
 			SELECT	rd.record,
 				rd.item_type,
@@ -556,7 +556,7 @@
 	if (lc($sort) eq 'pubdate') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(SUBSTRING(frp.value FROM '\\\\d+'),'9999')::INT
+				SELECT	COALESCE(SUBSTRING(frp.value FROM E'\\\\d+'),'9999')::INT
 				  FROM	$metabib_full_rec frp
 				  WHERE	frp.record = f.record
 				  	AND frp.tag = '260'
@@ -575,7 +575,7 @@
 	} elsif (lc($sort) eq 'title') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')
+				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')
 				  FROM	$metabib_full_rec frt
 				  WHERE	frt.record = f.record
 				  	AND frt.tag = '245'
@@ -1196,7 +1196,7 @@
 	if (lc($sort) eq 'pubdate') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(SUBSTRING(frp.value FROM '\\\\d+'),'$number_default_sort')::INT
+				SELECT	COALESCE(SUBSTRING(frp.value FROM E'\\\\d+'),'$number_default_sort')::INT
 				  FROM	$metabib_full_rec frp
 				  WHERE	frp.record = mr.master_record
 				  	AND frp.tag = '260'
@@ -1215,7 +1215,7 @@
 	} elsif (lc($sort) eq 'title') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
 				  FROM	$metabib_full_rec frt
 				  WHERE	frt.record = mr.master_record
 				  	AND frt.tag = '245'
@@ -1668,7 +1668,7 @@
 
 	my $secondary_sort = <<"	SORT";
 		( FIRST ((
-			SELECT	COALESCE(LTRIM(SUBSTR( sfrt.value, COALESCE(SUBSTRING(sfrt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+			SELECT	COALESCE(LTRIM(SUBSTR( sfrt.value, COALESCE(SUBSTRING(sfrt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
 			  FROM	$metabib_full_rec sfrt,
 				$metabib_metarecord mr
 			  WHERE	sfrt.record = mr.master_record
@@ -1682,7 +1682,7 @@
 	if (lc($sort) eq 'pubdate') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(SUBSTRING(frp.value FROM '\\\\d+'),'$number_default_sort')::INT
+				SELECT	COALESCE(SUBSTRING(frp.value FROM E'\\\\d+'),'$number_default_sort')::INT
 				  FROM	$metabib_full_rec frp
 				  WHERE	frp.record = mr.master_record
 				  	AND frp.tag = '260'
@@ -1701,7 +1701,7 @@
 	} elsif (lc($sort) eq 'title') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
 				  FROM	$metabib_full_rec frt
 				  WHERE	frt.record = mr.master_record
 				  	AND frt.tag = '245'
@@ -1711,7 +1711,7 @@
 		RANK
 		$secondary_sort = <<"		SORT";
 			( FIRST ((
-				SELECT	COALESCE(SUBSTRING(sfrp.value FROM '\\\\d+'),'$number_default_sort')::INT
+				SELECT	COALESCE(SUBSTRING(sfrp.value FROM E'\\\\d+'),'$number_default_sort')::INT
 				  FROM	$metabib_full_rec sfrp
 				  WHERE	sfrp.record = mr.master_record
 				  	AND sfrp.tag = '260'
@@ -2166,7 +2166,7 @@
 	if (lc($sort) eq 'pubdate') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(SUBSTRING(frp.value FROM '\\\\d{4}'),'$number_default_sort')::INT
+				SELECT	COALESCE(SUBSTRING(frp.value FROM E'\\\\d{4}'),'$number_default_sort')::INT
 				  FROM	$metabib_full_rec frp
 				  WHERE	frp.record = b.id
 				  	AND frp.tag = '260'
@@ -2185,7 +2185,7 @@
 	} elsif (lc($sort) eq 'title') {
 		$rank = <<"		RANK";
 			( FIRST ((
-				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+				SELECT	COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
 				  FROM	$metabib_full_rec frt
 				  WHERE	frt.record = b.id
 				  	AND frt.tag = '245'
@@ -2977,7 +2977,7 @@
 	my $metarecord = ($self->api_name =~ /metabib/ or $query->parse_tree->find_modifier('metabib') or $query->parse_tree->find_modifier('metarecord')) ? "'t'" : "'f'";
 
 	my $sth = metabib::metarecord_source_map->db_Main->prepare(<<"    SQL");
-        SELECT  *
+        SELECT  * /* bib search */
           FROM  search.query_parser_fts(
                     $param_search_ou\:\:INT,
                     $param_depth\:\:INT,

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/QueryParser.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/QueryParser.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Storage/QueryParser.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -442,7 +442,7 @@
 
             for my $alias ( @{$pkg->search_field_aliases->{$class}{$field}} ) {
                 $alias = qr/$alias/;
-                s/\b$alias[:=]/$class\|$field:/g;
+                s/(^|\s+)$alias[:=]/$1$class\|$field:/g;
             }
         }
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -305,7 +305,7 @@
 			},
 			{ flesh		=> 1,
 			  flesh_fields	=> { acn => [qw/record owning_lib/] },
-			  order_by	=> { acn => "label_sortkey, upper(label) desc, id desc, owning_lib desc" },
+			  order_by	=> { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(upper(label)) desc, id desc, owning_lib desc" },
 			  limit		=> $before_limit,
 			  offset	=> abs($page) * $page_size - $before_offset,
 			}
@@ -323,7 +323,7 @@
 			},
 			{ flesh		=> 1,
 			  flesh_fields	=> { acn => [qw/record owning_lib/] },
-			  order_by	=> { acn => "label_sortkey, upper(label), id, owning_lib" },
+			  order_by	=> { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(upper(label)), id, owning_lib" },
 			  limit		=> $after_limit,
 			  offset	=> abs($page) * $page_size - $after_offset,
 			}
@@ -428,7 +428,7 @@
 			},
 			{ flesh		=> 1,
 			  flesh_fields	=> { acn => [qw/record owning_lib/] },
-			  order_by	=> { acn => "label_sortkey, upper(label) desc, id desc, owning_lib desc" },
+			  order_by	=> { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(upper(label)) desc, id desc, owning_lib desc" },
 			  limit		=> $limit,
 			  offset	=> $offset,
 			}
@@ -446,7 +446,7 @@
 			},
 			{ flesh		=> 1,
 			  flesh_fields	=> { acn => [qw/record owning_lib/] },
-			  order_by	=> { acn => "label_sortkey, upper(label), id, owning_lib" },
+			  order_by	=> { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(upper(label)), id, owning_lib" },
 			  limit		=> $limit,
 			  offset	=> $offset,
 			}

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -2,13 +2,10 @@
 use strict; use warnings;
 use OpenSRF::EX qw/:try/;
 use OpenSRF::Utils::JSON;
-
 use OpenSRF::Utils::Logger qw/$logger/;
-
 use OpenILS::Utils::Fieldmapper;
 use OpenILS::Utils::CStoreEditor q/:funcs/;
 use OpenILS::Application::Trigger::ModRunner;
-
 use Safe;
 
 my $log = 'OpenSRF::Utils::Logger';
@@ -19,11 +16,17 @@
     my $editor = shift;
     $class = ref($class) || $class;
 
-    return $id if (ref($id) && ref($id) eq $class);
-
     my $standalone = $editor ? 0 : 1;
     $editor ||= new_editor();
 
+    if (ref($id) && ref($id) eq $class) {
+        $id->environment->{EventProcessor} = $id
+             if ($id->environment->{complete}); # in case it came over an opensrf tube
+        $id->editor( $editor );
+        $id->standalone( $standalone );
+        return $id;
+    }
+
     my $self = bless { id => $id, editor => $editor, standalone => $standalone } => $class;
 
     return $self->init()
@@ -45,6 +48,10 @@
 
     return $self if (!$self->id);
 
+    if ($self->standalone) {
+        $self->editor->xact_begin || return undef;
+    }
+
     $self->event(
         $self->editor->retrieve_action_trigger_event([
             $self->id, {
@@ -57,6 +64,10 @@
         ])
     );
 
+    if ($self->standalone) {
+        $self->editor->xact_rollback || return undef;
+    }
+
     $self->user_data(OpenSRF::Utils::JSON->JSON2perl( $self->event->user_data ))
         if (defined( $self->event->user_data ));
 
@@ -91,8 +102,21 @@
     $meth =~ s/Fieldmapper:://;
     $meth =~ s/::/_/;
     
+    if ($self->standalone) {
+        $self->editor->xact_begin || return undef;
+    }
+
     $self->target( $self->editor->$meth( $self->event->target ) );
 
+    if ($self->standalone) {
+        $self->editor->xact_rollback || return undef;
+    }
+
+    unless($self->target) {
+        $self->update_state('invalid');
+        $self->valid(0);
+    }
+
     return $self;
 }
 
@@ -336,6 +360,7 @@
     $e->state( $state );
 
     $e->clear_start_time() if ($e->state eq 'pending');
+    $e->complete_time( 'now' ) if ($e->state eq 'complete');
 
     my $ok = $self->editor->update_action_trigger_event( $e );
     if (!$ok) {
@@ -397,7 +422,8 @@
 
         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->target, undef, [], \@group_path );
+            pop(@group_path); # the last part is a field, should not get fleshed
+            my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path ) if (@group_path);
         }
     
         $self->environment->{complete} = 1;
@@ -426,17 +452,45 @@
     return $class;
 }
 
+my %_object_by_path_cache = ();
+sub ClearObjectCache {
+    for my $did ( keys %_object_by_path_cache ) {
+        my $phash = $_object_by_path_cache{$did};
+        for my $path ( keys %$phash ) {
+            my $shash = $$phash{$path};
+            for my $step ( keys %$shash ) {
+                my $fhash = $$shash{$step};
+                for my $ffield ( keys %$fhash ) {
+                    my $lhash = $$fhash{$ffield};
+                    for my $lfield ( keys %$lhash ) {
+                        delete $$lhash{$lfield};
+                    }
+                    delete $$fhash{$ffield};
+                }
+                delete $$shash{$step};
+            }
+            delete $$phash{$path};
+        }
+        delete $_object_by_path_cache{$did};
+    }
+}
+        
 sub _object_by_path {
     my $self = shift;
     my $context = shift;
     my $collector = shift;
     my $label = shift;
     my $path = shift;
+    my $ed = shift;
 
+    my $outer = 0;
+    if (!$ed) {
+        $ed = new_editor(xact=>1);
+        $outer = 1;
+    }
 
     my $step = shift(@$path);
 
-
     my $fhint = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
     my $fclass = $self->_fm_class_by_hint( $fhint );
 
@@ -460,10 +514,6 @@
     $meth =~ s/Fieldmapper:://;
     $meth =~ s/::/_/g;
 
-    my $ed = grep( /open-ils.cstore/, @{$fclass->Controller} ) ?
-            $self->editor :
-            new_rstore_editor();
-
     my $obj = $context->$step(); 
 
     $logger->debug(
@@ -472,11 +522,19 @@
     );
 
     if (!ref $obj) {
-        $obj = $ed->$meth( 
-            ($multi) ?
-                { $ffield => $context->$lfield() } :
-                $context->$lfield()
-        );
+
+        my $lval = $context->$lfield();
+
+        if(defined $lval) {
+
+            my $def_id = $self->event->event_def->id;
+            my $str_path = join('.', @$path);
+
+            $obj = $_object_by_path_cache{$def_id}{$str_path}{$step}{$ffield}{$lval} ||
+                $ed->$meth( ($multi) ? { $ffield => $lval } : $lval);
+
+            $_object_by_path_cache{$def_id}{$str_path}{$step}{$ffield}{$lval} ||= $obj;
+        }
     }
 
     if (@$path) {
@@ -490,7 +548,7 @@
 
         for (@$obj_list) {
             my @path_clone = @$path;
-            $self->_object_by_path( $_, $collector, $label, \@path_clone );
+            $self->_object_by_path( $_, $collector, $label, \@path_clone, $ed );
         }
 
         $obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
@@ -533,6 +591,7 @@
         }
     }
 
+    $ed->rollback if ($outer);
     return $obj;
 }
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -28,7 +28,7 @@
                     OpenILS::Application::Trigger::Event->new($_, $editor)
             } @ids
         ],
-        ids         => \@ids,
+        ids         => [ map { ref($_) ? $_->id : $_ } @ids ],
         editor      => $editor
     } => $class;
 
@@ -212,11 +212,11 @@
     if (scalar(@oks) < scalar(@{ $self->ids })) {
         $self->editor->xact_rollback;
         return undef;
-    } else {
-        $ok = $self->editor->xact_commit;
-    }
+    } 
 
     my $updated = $self->editor->retrieve_action_trigger_event($last_updated);
+    $ok = $self->editor->xact_commit;
+
     if ($ok) {
         for my $event ( @{ $self->events } ) {
             my $e = $event->event;

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Reactor.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Reactor.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Reactor.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -57,13 +57,13 @@
     # returns the calculated copy price
     get_copy_price => sub {
         my $copy_id = shift;
-        return $U->get_copy_price(new_editor(), $copy_id);
+        return $U->get_copy_price(new_editor(xact=>1), $copy_id);
     },
 
     # given a copy, returns the title and author in a hash
     get_copy_bib_basics => sub {
         my $copy_id = shift;
-        my $copy = new_editor()->retrieve_asset_copy([
+        my $copy = new_editor(xact=>1)->retrieve_asset_copy([
             $copy_id,
             {
                 flesh => 2,
@@ -156,6 +156,7 @@
         my $t_o = Fieldmapper::action_trigger::event_output->new;
         $t_o->data( ($error) ? $error : $output );
         $t_o->is_error( ($error) ? 't' : 'f' );
+        $logger->info("trigger: writing " . length($t_o->data) . " bytes to template output");
 
         $env->{EventProcessor}->editor->xact_begin;
         $t_o = $env->{EventProcessor}->editor->create_action_trigger_event_output( $t_o );

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -7,6 +7,7 @@
 use OpenSRF::Utils::JSON;
 
 use OpenSRF::AppSession;
+use OpenSRF::MultiSession;
 use OpenSRF::Utils::SettingsClient;
 use OpenSRF::Utils::Logger qw/$logger/;
 use OpenSRF::Utils qw/:datetime/;
@@ -21,8 +22,16 @@
 
 
 my $log = 'OpenSRF::Utils::Logger';
+my $parallel_collect;
+my $parallel_react;
 
-sub initialize {}
+sub initialize {
+
+    my $conf = OpenSRF::Utils::SettingsClient->new;
+    $parallel_collect = $conf->config_value( apps => 'open-ils.trigger' => app_settings => parallel => 'collect') || 1;
+    $parallel_react = $conf->config_value( apps => 'open-ils.trigger' => app_settings => parallel => 'react') || 1;
+
+}
 sub child_init {}
 
 sub create_active_events_for_object {
@@ -68,7 +77,7 @@
             my $uid = $target->$ufield;
             $uid = $uid->id if (ref $uid); # fleshed user object, unflesh it
 
-            my $opt_in_setting = $editor->search_actor_usr_setting(
+            my $opt_in_setting = $editor->search_actor_user_setting(
                 { usr   => $uid,
                   name  => $def->opt_in_setting,
                   value => 'true'
@@ -154,7 +163,7 @@
             my $uid = $target->$ufield;
             $uid = $uid->id if (ref $uid); # fleshed user object, unflesh it
 
-            my $opt_in_setting = $editor->search_actor_usr_setting(
+            my $opt_in_setting = $editor->search_actor_user_setting(
                 { usr   => $uid,
                   name  => $def->opt_in_setting,
                   value => 'true'
@@ -328,7 +337,7 @@
             for (grep { $_ ne '-and' } keys %{$$filter{event}});
     }
 
-    my $e = new_editor();
+    my $e = new_editor(xact=>1);
 
     my $events = $e->json_query($query);
 
@@ -469,7 +478,7 @@
                 '-exists' => {
                     from  => 'aus',
                     where => {
-                        name => $def->id,
+                        name => $def->opt_in_setting,
                         usr  => { '=' => { '+' . $hook_hash{$def->hook}->core_type => $def->usr_field } },
                         value=> 'true'
                     }
@@ -502,7 +511,6 @@
             $event->event_def( $def->id );
             $event->run_time( $run_time );
             $event->user_data( OpenSRF::Utils::JSON->perl2JSON($user_data) ) if (defined($user_data));
-            $event->granularity($granularity) if (defined $granularity);
 
             $editor->create_action_trigger_event( $event );
 
@@ -547,6 +555,7 @@
     }
 
     $e->editor->disconnect;
+    OpenILS::Application::Trigger::Event->ClearObjectCache();
 
     return {
         valid     => $e->valid,
@@ -575,6 +584,7 @@
     }
 
     $e->editor->disconnect;
+    OpenILS::Application::Trigger::Event->ClearObjectCache();
 
     return {
         valid     => $e->valid,
@@ -594,18 +604,21 @@
     my $self = shift;
     my $client = shift;
     my $granularity = shift;
+    my $granflag = shift;
 
-    my $editor = new_editor();
-
     my $query = [{ state => 'pending', run_time => {'<' => 'now'} }, { order_by => { atev => [ qw/run_time add_time/] }, 'join' => 'atevdef' }];
 
     if (defined $granularity) {
-        $query->[0]->{'+atevdef'} = {'-or' => [ {granularity => $granularity}, {granularity => undef} ] };
+        if ($granflag) {
+            $query->[0]->{'+atevdef'} = {granularity => $granularity};
+        } else {
+            $query->[0]->{'+atevdef'} = {'-or' => [ {granularity => $granularity}, {granularity => undef} ] };
+        }
     } else {
         $query->[0]->{'+atevdef'} = {granularity => undef};
     }
 
-    return $editor->search_action_trigger_event(
+    return new_editor(xact=>1)->search_action_trigger_event(
         $query, { idlist=> 1, timeout => 7200, substream => 1 }
     );
 }
@@ -615,12 +628,52 @@
     api_level=> 1
 );
 
+sub gather_events {
+    my $self = shift;
+    my $client = shift;
+    my $e_ids = shift;
+
+    $e_ids = [$e_ids] if (!ref($e_ids));
+
+    my @events;
+    for my $e_id (@$e_ids) {
+        my $e;
+        try {
+           $e = OpenILS::Application::Trigger::Event->new($e_id);
+        } catch Error with {
+            $logger->error("trigger: Event creation failed with ".shift());
+        };
+
+        next if !$e or $e->event->state eq 'invalid'; 
+
+        try {
+            $e->build_environment;
+        } catch Error with {
+            $logger->error("trigger: Event environment building failed with ".shift());
+        };
+
+        $e->editor->disconnect;
+        $e->environment->{EventProcessor} = undef; # remove circular ref for json encoding
+        $client->respond($e);
+    }
+
+    OpenILS::Application::Trigger::Event->ClearObjectCache();
+
+    return undef;
+}
+__PACKAGE__->register_method(
+    api_name => 'open-ils.trigger.event.gather',
+    method   => 'gather_events',
+    api_level=> 1
+);
+
 sub grouped_events {
     my $self = shift;
     my $client = shift;
     my $granularity = shift;
+    my $granflag = shift;
 
-    my ($events) = $self->method_lookup('open-ils.trigger.event.find_pending')->run($granularity);
+    my ($events) = $self->method_lookup('open-ils.trigger.event.find_pending')->run($granularity, $granflag);
 
     my %groups = ( '*' => [] );
 
@@ -631,51 +684,67 @@
         return \%groups;
     }
 
-    for my $e_id ( @$events ) {
-        $logger->info("trigger: processing event $e_id");
+    my @fleshed_events;
 
-        # let the client know we're still chugging along TODO add osrf support for method_lookup $client's
-        $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+    if ($parallel_collect == 1 or @$events == 1) { # use method lookup
+        @fleshed_events = $self->method_lookup('open-ils.trigger.event.gather')->run($events);
+    } else {
+        my $self_multi = OpenSRF::MultiSession->new(
+            app                 => 'open-ils.trigger',
+            cap                 => $parallel_collect,
+            success_handler     => sub {
+                my $self = shift;
+                my $req = shift;
 
-        my $e;
-        try {
-           $e = OpenILS::Application::Trigger::Event->new($e_id);
-        } catch Error with {
-            $logger->error("trigger: Event creation failed with ".shift());
-        };
+                push @fleshed_events,
+                    map { OpenILS::Application::Trigger::Event->new($_) }
+                    map { $_->content }
+                    @{ $req->{response} };
+            },
+        );
 
-        next unless $e; 
+        $self_multi->request( 'open-ils.trigger.event.gather' => $_ ) for ( @$events );
+        $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
 
-        try {
-            $e->build_environment;
-        } catch Error with {
-            $logger->error("trigger: Event environment building failed with ".shift());
-        };
+        $self_multi->session_wait(1);
+        $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+    }
 
+    for my  $e (@fleshed_events) {
         if (my $group = $e->event->event_def->group_field) {
 
             # split the grouping link steps
             my @steps = split /\./, $group;
+            my $group_field = pop(@steps); # we didn't flesh to this, it's a field not an object
 
-            # find the grouping object
-            my $node = $e->target;
-            $node = $node->$_() for ( @steps );
+            my $node;
+            eval {
+                $node = $e->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();
+            unless($node) { # should not get here, but to be safe..
+                $e->update_state('invalid');
+                next;
+            }
 
-            # push this event onto the event+grouping_pkey_value stack
+            # get the grouping value for the grouping object on this event
+            my $ident_value = $node->$group_field();
+            if(ref $ident_value) {
+                my $ident_field = $ident_value->Identity; 
+                $ident_value = $ident_value->$ident_field()
+            }
+
+            # push this event onto the event+grouping_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;
         }
-
-        $e->editor->disconnect;
     }
 
+
     return \%groups;
 }
 __PACKAGE__->register_method(
@@ -688,44 +757,80 @@
     my $self = shift;
     my $client = shift;
     my $granularity = shift;
+    my $granflag = shift;
 
-    my ($groups) = $self->method_lookup('open-ils.trigger.event.find_pending_by_group')->run($granularity);
+    my ($groups) = $self->method_lookup('open-ils.trigger.event.find_pending_by_group')->run($granularity, $granflag);
+    $client->respond({"status" => "found"}) if (keys(%$groups) > 1 || @{$$groups{'*'}});
 
+    my $self_multi;
+    if ($parallel_react > 1 and (keys(%$groups) > 1 || @{$$groups{'*'}} > 1)) {
+        $self_multi = OpenSRF::MultiSession->new(
+            app                   => 'open-ils.trigger',
+            cap                   => $parallel_react,
+            session_hash_function => sub {
+                my $args = shift;
+                return $args->{target_id};
+            },
+            success_handler       => sub {
+                my $me = shift;
+                my $req = shift;
+                $client->respond( $req->{response}->[0]->content );
+            }
+        );
+    }
+
     for my $def ( keys %$groups ) {
         if ($def eq '*') {
             $logger->info("trigger: run_all_events firing un-grouped events");
             for my $event ( @{ $$groups{'*'} } ) {
                 try {
-                    $client->respond(
-                        $self
-                            ->method_lookup('open-ils.trigger.event.fire')
-                            ->run($event)
-                    );
+                    if ($self_multi) {
+                        $event->environment->{EventProcessor} = undef; # remove circular ref for json encoding
+                        $self_multi->request({target_id => $event->id}, 'open-ils.trigger.event.fire', $event);
+                    } else {
+                        $client->respond(
+                            $self
+                                ->method_lookup('open-ils.trigger.event.fire')
+                                ->run($event)
+                        );
+                    }
                 } catch Error with { 
                     $logger->error("trigger: event firing failed with ".shift());
                 };
             }
-            $logger->info("trigger: run_all_events completed firing un-grouped events");
+            $logger->info("trigger: run_all_events completed queuing un-grouped events");
+            $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
 
         } else {
             my $defgroup = $$groups{$def};
             $logger->info("trigger: run_all_events firing events for grouped event def=$def");
             for my $ident ( keys %$defgroup ) {
+                $logger->info("trigger: run_all_events firing group for grouped event def=$def and grp ident $ident");
                 try {
-                    $client->respond(
-                        $self
-                            ->method_lookup('open-ils.trigger.event_group.fire')
-                            ->run($$defgroup{$ident})
-                    );
+                    if ($self_multi) {
+                        $_->environment->{EventProcessor} = undef for @{$$defgroup{$ident}}; # remove circular ref for json encoding
+                        $self_multi->request({target_id => $ident}, 'open-ils.trigger.event_group.fire', $$defgroup{$ident});
+                    } else {
+                        $client->respond(
+                            $self
+                                ->method_lookup('open-ils.trigger.event_group.fire')
+                                ->run($$defgroup{$ident})
+                        );
+                    }
+                    $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
                 } catch Error with {
                     $logger->error("trigger: event firing failed with ".shift());
                 };
             }
-            $logger->info("trigger: run_all_events completed firing events for grouped event def=$def");
+            $logger->info("trigger: run_all_events completed queuing events for grouped event def=$def");
         }
     }
-                
-            
+
+    $self_multi->session_wait(1) if ($self_multi);
+    $logger->info("trigger: run_all_events completed firing events");
+
+    $client->respond_complete();
+    return undef;
 }
 __PACKAGE__->register_method(
     api_name => 'open-ils.trigger.event.run_all_pending',

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Vandelay.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Vandelay.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Application/Vandelay.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -68,9 +68,10 @@
     return $e->die_event unless $e->allowed('CREATE_BIB_IMPORT_QUEUE');
     $owner ||= $e->requestor->id;
 
-    return OpenILS::Event->new('BIB_QUEUE_EXISTS') 
-        if $e->search_vandelay_bib_queue(
-            {name => $name, owner => $owner, queue_type => $type})->[0];
+    if ($e->search_vandelay_bib_queue( {name => $name, owner => $owner, queue_type => $type})->[0]) {
+        $e->rollback;
+        return OpenILS::Event->new('BIB_QUEUE_EXISTS') 
+    }
 
     my $queue = new Fieldmapper::vandelay::bib_queue();
     $queue->name( $name );
@@ -106,9 +107,10 @@
     return $e->die_event unless $e->allowed('CREATE_AUTHORITY_IMPORT_QUEUE');
     $owner ||= $e->requestor->id;
 
-    return OpenILS::Event->new('AUTH_QUEUE_EXISTS') 
-        if $e->search_vandelay_bib_queue(
-            {name => $name, owner => $owner, queue_type => $type})->[0];
+    if ($e->search_vandelay_bib_queue({name => $name, owner => $owner, queue_type => $type})->[0]) {
+        $e->rollback;
+        return OpenILS::Event->new('AUTH_QUEUE_EXISTS') 
+    }
 
     my $queue = new Fieldmapper::vandelay::authority_queue();
     $queue->name( $name );
@@ -242,7 +244,7 @@
     }
 
     my $evt = check_queue_perms($e, $type, $queue);
-    return $evt if $evt;
+    return $evt if ($evt);
 
     my $cache = new OpenSRF::Utils::Cache();
 
@@ -395,8 +397,8 @@
 
 sub retrieve_queued_records {
     my($self, $conn, $auth, $queue_id, $options) = @_;
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
     $options ||= {};
     my $limit = $$options{limit} || 20;
     my $offset = $$options{offset} || 0;
@@ -409,7 +411,7 @@
         $queue = $e->retrieve_vandelay_authority_queue($queue_id) or return $e->die_event;
     }
     my $evt = check_queue_perms($e, $type, $queue);
-    return $evt if $evt;
+    return $evt if ($evt);
 
     my $class = ($type eq 'bib') ? 'vqbr' : 'vqar';
     my $search = ($type eq 'bib') ? 
@@ -443,6 +445,7 @@
         $rec->clear_marc if $$options{clear_marc};
         $conn->respond($rec);
     }
+    $e->rollback;
     return undef;
 }
 
@@ -481,10 +484,11 @@
 
 sub import_record_list {
     my($self, $conn, $auth, $rec_ids, $args) = @_;
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
     $args ||= {};
     my $err = import_record_list_impl($self, $conn, $rec_ids, $e->requestor, $args);
+    $e->rollback;
     return $err if $err;
     return {complete => 1};
 }
@@ -532,8 +536,8 @@
 );
 sub import_queue {
     my($self, $conn, $auth, $q_id, $options) = @_;
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
+    my $e = new_editor(authtoken => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
     $options ||= {};
     my $type = $self->{record_type};
     my $class = ($type eq 'bib') ? 'vqbr' : 'vqar';
@@ -549,6 +553,7 @@
         'search_vandelay_queued_bib_record' : 'search_vandelay_queued_authority_record';
     my $rec_ids = $e->$search($query, {idlist => 1});
     my $err = import_record_list_impl($self, $conn, $rec_ids, $e->requestor, $options);
+    $e->rollback;
     return $err if $err;
     return {complete => 1};
 }
@@ -620,6 +625,7 @@
     my %bib_sources;
     my $editor = new_editor();
     my $sources = $editor->search_config_bib_source({id => {'!=' => undef}});
+
     foreach my $src (@$sources) {
         $bib_sources{$src->id} = $src->source;
     }
@@ -635,6 +641,7 @@
         $rec_class = 'vqar';
     }
 
+    my @success_rec_ids;
     for my $rec_id (@$rec_ids) {
 
         my $overlay_target = $overlay_map->{$rec_id};
@@ -650,7 +657,7 @@
         ]);
 
         unless($rec) {
-            $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->die_event});
+            $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->event});
             $e->rollback;
             next;
         }
@@ -746,6 +753,7 @@
                 if($U->event_code($record)) {
 
                     $e->event($record); 
+                    $e->rollback;
 
                 } else {
 
@@ -759,11 +767,11 @@
         }
 
         if($imported) {
+            push @success_rec_ids, $rec_id;
             $e->commit;
         } else {
-            $e->rollback;
             # Send an update whenever there's an error
-            $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->die_event});
+            $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->event});
         }
 
         $conn->respond({total => $total, progress => $count, imported => $rec_id}) if (!$report_all and ++$count % $step) == 0;
@@ -789,6 +797,8 @@
     	$e->rollback;
     }
 
+    import_record_asset_list_impl($conn, \@success_rec_ids, $requestor);
+
     $conn->respond({total => $total, progress => $count});
     return undef;
 }
@@ -813,7 +823,7 @@
 
 sub owner_queue_retrieve {
     my($self, $conn, $auth, $owner_id, $filters) = @_;
-    my $e = new_editor(authtoken => $auth);
+    my $e = new_editor(authtoken => $auth, xact => 1);
     return $e->die_event unless $e->checkauth;
     $owner_id = $e->requestor->id; # XXX add support for viewing other's queues?
     my $queues;
@@ -829,6 +839,7 @@
             [$search, {order_by => {vaq => 'lower(name)'}}]);
     }
     $conn->respond($_) for @$queues;
+    $e->rollback;
     return undef;
 }
 
@@ -888,17 +899,18 @@
 
 sub queued_record_html {
     my($self, $conn, $auth, $rec_id) = @_;
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
+    my $e = new_editor(xact=>1,authtoken => $auth);
+    return $e->die_event unless $e->checkauth;
     my $rec;
     if($self->{record_type} eq 'bib') {
         $rec = $e->retrieve_vandelay_queued_bib_record($rec_id)
-            or return $e->event;
+            or return $e->die_event;
     } else {
         $rec = $e->retrieve_vandelay_queued_authority_record($rec_id)
-            or return $e->event;
+            or return $e->die_event;
     }
 
+    $e->rollback;
     return $U->simplereq(
         'open-ils.search',
         'open-ils.search.biblio.record.html', undef, 1, $rec->marc);
@@ -924,17 +936,17 @@
 
 sub retrieve_queue_summary {
     my($self, $conn, $auth, $queue_id) = @_;
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
+    my $e = new_editor(xact=>1, authtoken => $auth);
+    return $e->die_event unless $e->checkauth;
 
     my $queue;
     my $type = $self->{record_type};
     if($type eq 'bib') {
         $queue = $e->retrieve_vandelay_bib_queue($queue_id)
-            or return $e->event;
+            or return $e->die_event;
     } else {
         $queue = $e->retrieve_vandelay_authority_queue($queue_id)
-            or return $e->event;
+            or return $e->die_event;
     }
 
     my $evt = check_queue_perms($e, $type, $queue);
@@ -953,7 +965,7 @@
 
 __PACKAGE__->register_method(  
     api_name    => "open-ils.vandelay.bib_record.list.asset.import",
-    method      => 'import_record_list_assets',
+    method      => 'noop_import_items',
     api_level   => 1,
     argc        => 2,
     stream      => 1,
@@ -961,48 +973,52 @@
 );
 __PACKAGE__->register_method(  
     api_name    => "open-ils.vandelay.bib_record.queue.asset.import",
-    method      => 'import_record_queue_assets',
+    method      => 'noop_import_items',
     api_level   => 1,
     argc        => 2,
     stream      => 1,
     record_type => 'bib'
 );
 
-sub import_record_list_assets {
-    my($self, $conn, $auth, $import_def, $rec_ids) = @_;
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
-    my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
-    return $err if $err;
-    return {complete => 1};
-}
+sub noop_import_items { return {complete => 1} }
 
-sub import_record_queue_assets {
-    my($self, $conn, $auth, $import_def, $q_id) = @_;
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
-    my $rec_ids = $e->search_vandelay_queued_bib_record(
-        {queue => $q_id, import_time => {'!=' => undef}}, {idlist => 1});
-    my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
-    return $err if $err;
-    return {complete => 1};
-}
+#sub import_record_list_assets {
+#    my($self, $conn, $auth, $import_def, $rec_ids) = @_;
+#    my $e = new_editor(xact=>1, authtoken => $auth);
+#    return $e->die_event unless $e->checkauth;
+#    my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
+#    $e->rollback;
+#    return $err if $err;
+#    return {complete => 1};
+#}
+#
+#sub import_record_queue_assets {
+#    my($self, $conn, $auth, $import_def, $q_id) = @_;
+#    my $e = new_editor(xact=>1, authtoken => $auth);
+#    return $e->die_event unless $e->checkauth;
+#    my $rec_ids = $e->search_vandelay_queued_bib_record(
+#        {queue => $q_id, import_time => {'!=' => undef}}, {idlist => 1});
+#    my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
+#    $e->rollback;
+#    return $err if $err;
+#    return {complete => 1};
+#}
 
 # --------------------------------------------------------------------------------
 # Given a list of queued record IDs, imports all items attached to those records
 # --------------------------------------------------------------------------------
 sub import_record_asset_list_impl {
-    my($conn, $import_def, $rec_ids, $requestor) = @_;
+    my($conn, $rec_ids, $requestor) = @_;
 
     my $total = @$rec_ids;
     my $try_count = 0;
     my $in_count = 0;
-    my $roe = new_editor(requestor => $requestor);
+    my $roe = new_editor(xact=> 1, requestor => $requestor);
 
     for my $rec_id (@$rec_ids) {
         my $rec = $roe->retrieve_vandelay_queued_bib_record($rec_id);
         next unless $rec and $rec->import_time;
-        my $item_ids = $roe->search_vandelay_import_item({definition => $import_def, record => $rec->id}, {idlist=>1});
+        my $item_ids = $roe->search_vandelay_import_item({record => $rec->id}, {idlist=>1});
 
         for my $item_id (@$item_ids) {
             my $e = new_editor(requestor => $requestor, xact => 1);
@@ -1087,6 +1103,7 @@
             respond_with_status($conn, $total, $try_count, ++$in_count, undef, imported_as => $copy->id);
         }
     }
+    $roe->rollback;
     return undef;
 }
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Item.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Item.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Item.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -125,13 +125,16 @@
         syslog('LOG_DEBUG', "OILS: Open circulation exists on $item_id : user = $bc");
     }
 
-    $self->{id} = $item_id;
-    $self->{copy}        = $copy;
-    $self->{volume}      = $copy->call_number;
-    $self->{record}      = $copy->call_number->record;
+    $self->{id}         = $item_id;
+    $self->{copy}       = $copy;
+    $self->{volume}     = $copy->call_number;
+    $self->{record}     = $copy->call_number->record;
     $self->{call_number} = $copy->call_number->label;
-    $self->{mods} = $U->record_to_mvr($self->{record}) if $self->{record}->marc;
+    $self->{mods}       = $U->record_to_mvr($self->{record}) if $self->{record}->marc;
+    $self->{transit}    = $self->fetch_transit;
+    $self->{hold}       = $self->fetch_hold;
 
+
     # use the non-translated version of the copy location as the
     # collection code, since it may be used for additional routing
     # purposes by the SIP client.  Config option?
@@ -139,26 +142,12 @@
         $e->retrieve_asset_copy_location([
             $copy->location, {no_i18n => 1}])->name;
 
-    if ($copy->status->id == OILS_COPY_STATUS_IN_TRANSIT) {
-        my $transit = $e->search_action_transit_copy([
-            {
-                target_copy    => $copy->id,    # NOT barcode ($self->id)
-                dest_recv_time => undef
-            },
-            {
-                flesh => 1,
-                flesh_fields => {
-                    atc => [ 'dest' ]
-                }
-            }
-        ])->[0];
 
-        if ($transit) {
-            $self->{transit} = $transit;
-            $self->{destination_loc} = $transit->dest->shortname;
-        } else {
-            syslog('LOG_WARNING', "OILS: Item('$item_id') status is In Transit, but no action.transit_copy found!");
-        }
+    if($self->{transit}) {
+        $self->{destination_loc} = $self->{transit}->dest->shortname;
+
+    } elsif($self->{hold}) {
+        $self->{destination_loc} = $self->{hold}->pickup_lib->shortname;
     }
 
     syslog("LOG_DEBUG", "OILS: Item('$item_id'): found with title '%s'", $self->title_id);
@@ -182,6 +171,71 @@
     return $self;
 }
 
+# fetch copy transit
+sub fetch_transit {
+    my $self = shift;
+    my $copy = $self->{copy} or return;
+    my $e = OpenILS::SIP->editor();
+
+    if ($copy->status->id == OILS_COPY_STATUS_IN_TRANSIT) {
+        my $transit = $e->search_action_transit_copy([
+            {
+                target_copy    => $copy->id,    # NOT barcode ($self->id)
+                dest_recv_time => undef
+            },
+            {
+                flesh => 1,
+                flesh_fields => {
+                    atc => ['dest']
+                }
+            }
+        ])->[0];
+
+        syslog('LOG_WARNING', "OILS: Item(".$copy->barcode.
+            ") status is In Transit, but no action.transit_copy found!") unless $transit;
+            
+        return $transit;
+    }
+    
+    return undef;
+}
+
+# fetch captured hold.
+# Assume transit has already beeen fetched
+sub fetch_hold {
+    my $self = shift;
+    my $copy = $self->{copy} or return;
+    my $e = OpenILS::SIP->editor();
+
+    if( ($copy->status->id == OILS_COPY_STATUS_ON_HOLDS_SHELF) ||
+        ($self->{transit} and $self->{transit}->copy_status == OILS_COPY_STATUS_ON_HOLDS_SHELF) ) {
+        # item has been captured for a hold
+
+        my $hold = $e->search_action_hold_request([
+            {
+                current_copy        => $copy->id,
+                capture_time        => {'!=' => undef},
+                cancel_time         => undef,
+                fulfillment_time    => undef
+            },
+            {
+                limit => 1,
+                flesh => 1,
+                flesh_fields => {
+                    ahr => ['pickup_lib']
+                }
+            }
+        ])->[0];
+
+        syslog('LOG_WARNING', "OILS: Item(".$copy->barcode.
+            ") is captured for a hold, but there is no matching hold request") unless $hold;
+
+        return $hold;
+    }
+
+    return undef;
+}
+
 sub run_attr_script {
 	my $self = shift;
 	return 1 if $self->{ran_script};
@@ -373,47 +427,29 @@
 sub hold_pickup_date {  
     my $self = shift;
     my $copy = $self->{copy};
+    my $hold = $self->{hold} or return 0;
 
-    if( ($copy->status->id == OILS_COPY_STATUS_ON_HOLDS_SHELF) ||
-        ($self->{transit} and $self->{transit}->copy_status == OILS_COPY_STATUS_ON_HOLDS_SHELF) ) {
+    my $date = $hold->shelf_expire_time;
 
-        # item has been captured for a hold
+    if(!$date) {
+        # hold has not hit the shelf.  create a best guess.
 
-        my $e = OpenILS::SIP->editor();
-        my $hold = $e->search_action_hold_request([
-            {
-                current_copy        => $copy->id,
-                capture_time        => {'!=' => undef},
-                cancel_time         => undef,
-                fulfillment_time    => undef
-            },
-            {limit => 1}
-        ])->[0];
-        
-        if($hold) {
-            my $date = $hold->shelf_expire_time;
+        my $interval = $shelf_expire_setting_cache{$hold->pickup_lib->id} ||
+            $U->ou_ancestor_setting_value(
+                $hold->pickup_lib->id, 
+                'circ.holds.default_shelf_expire_interval');
 
-            if(!$date) {
-                # hold has not hit the shelf.  create a best guess.
+        $shelf_expire_setting_cache{$hold->pickup_lib->id} = $interval;
 
-                my $interval = $shelf_expire_setting_cache{$hold->pickup_lib} ||
-                    $U->ou_ancestor_setting_value(
-                        $hold->pickup_lib, 
-                        'circ.holds.default_shelf_expire_interval');
-
-                $shelf_expire_setting_cache{$hold->pickup_lib} = $interval;
-
-                if($interval) {
-                    my $seconds = OpenSRF::Utils->interval_to_seconds($interval);
-                    $date = DateTime->now->add(seconds => $seconds);
-                    $date = $date->strftime('%FT%T%z') if $date;
-                }
-            }
-
-            return OpenILS::SIP->format_date($date) if $date;
+        if($interval) {
+            my $seconds = OpenSRF::Utils->interval_to_seconds($interval);
+            $date = DateTime->now->add(seconds => $seconds);
+            $date = $date->strftime('%FT%T%z') if $date;
         }
     }
 
+    return OpenILS::SIP->format_date($date) if $date;
+
     return 0;
 }
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Patron.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Patron.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Patron.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -41,46 +41,49 @@
 
     if ($key ne 'usr' and $key ne 'barcode') {
         syslog("LOG_ERROR", "Patron (card) lookup requested by illegeal key '$key'");
-        return;
+        return undef;
     }
 
+    unless(defined $patron_id) {
+        syslog("LOG_WARNING", "No patron ID provided to ILS::Patron->new");
+        return undef;
+    }
+
     my $type = ref($class) || $class;
     my $self = {};
 
     syslog("LOG_DEBUG", "OILS: new OpenILS Patron(%s => %s): searching...", $key, $patron_id);
 
     my $e = OpenILS::SIP->editor();
+    
+    my $user_id = $patron_id;
+    if($key eq 'barcode') {
+        my $card = $e->search_actor_card({barcode => $patron_id})->[0];
+        unless($card) {
+            syslog("LOG_WARNING", "No such patron barcode: $patron_id");
+            return undef;
+        }
+        $user_id = $card->usr;
+    }
 
-    my $c = $e->search_actor_card({$key => $patron_id}, {idlist=>1});
-	my $user;
+	my $user = $e->retrieve_actor_user([
+        $user_id,
+        {
+            flesh => 2,
+            flesh_fields => {
+                au => [
+                    "card",
+                    "standing_penalties",
+                    "addresses",
+                    "billing_address",
+                    "mailing_address",
+                    'profile',
+                ],
+                ausp => ['standing_penalty']
+            }
+        }
+    ]);
 
-	if( @$c ) {
-
-		$user = $e->search_actor_user(
-			[
-				{ card => $$c[0] },
-				{
-					flesh => 2,
-					flesh_fields => {
-						"au" => [
-							#"cards",
-							"card",
-							"standing_penalties",
-							"addresses",
-							"billing_address",
-							"mailing_address",
-							#"stat_cat_entries",
-							'profile',
-						],
-                        ausp => ['standing_penalty']
-					}
-				}
-			]
-		);
-
-		$user = (@$user) ? $$user[0] : undef;
-	 }
-
     if(!$user) {
         syslog("LOG_WARNING", "OILS: Unable to find patron %s => %s", $key, $patron_id);
         return undef;
@@ -218,20 +221,19 @@
     return $self->{user}->card->active eq 'f';
 }
 
-sub recall_overdue {
+sub recall_overdue {        # not implemented
     my $self = shift;
     return 0;
 }
 
-
 sub check_password {
 	my ($self, $pwd) = @_;
 	syslog('LOG_DEBUG', 'OILS: Patron->check_password()');
+    return 0 unless (defined $pwd and $self->{user});
 	return md5_hex($pwd) eq $self->{user}->passwd;
 }
 
-
-sub currency {
+sub currency {              # not really implemented
 	my $self = shift;
 	syslog('LOG_DEBUG', 'OILS: Patron->currency()');
 	return 'USD';
@@ -280,12 +282,12 @@
 	return 'OK';
 }
 
-sub print_line {
+sub print_line {            # not implemented
     my $self = shift;
 	return '';
 }
 
-sub too_many_charged {
+sub too_many_charged {      # not implemented
     my $self = shift;
 	return 0;
 }

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Transaction/Checkin.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Transaction/Checkin.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP/Transaction/Checkin.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -44,6 +44,8 @@
 
     @{$self}{keys %fields} = values %fields;        # copying defaults into object
 
+    $self->load_override_events;
+
     return bless $self, $class;
 }
 
@@ -53,6 +55,16 @@
     return !$self->{item}->magnetic;
 }
 
+my %override_events;
+sub load_override_events {
+    return if %override_events;
+    my $override = OpenILS::SIP->config->{implementation_config}->{checkin_override};
+    return unless $override;
+    my $events = $override->{event};
+    $events = [$events] unless ref $events eq 'ARRAY';
+    $override_events{$_} = 1 for @$events;
+}
+
 my %org_sn_cache;
 sub do_checkin {
     my $self = shift;
@@ -71,6 +83,13 @@
 
     my $args = {barcode => $self->{item}->id};
 
+    if($return_date) {
+        # SIP date format is YYYYMMDD.  Translate to ISO8601
+        $return_date =~ s/(\d{4})(\d{2})(\d{2}).*/$1-$2-$3/;
+        syslog('LOG_INFO', "Checking in with backdate $return_date");
+        $args->{backdate} = $return_date;
+    }
+
     if($current_loc) { # SIP client specified a physical location
 
         my $org_id = (defined $org_sn_cache{$current_loc}) ? 
@@ -83,25 +102,37 @@
         $args->{circ_lib} = $phys_location = $org_id if defined $org_id;
     }
 
-    my $resp = $U->simplereq(
-        'open-ils.circ',
-        'open-ils.circ.checkin',
-        $self->{authtoken}, $args
-    );
+    my $override = 0;
+    my ($resp, $txt, $code);
 
-    if ($debug) {
-        my $s = Dumper($resp);
-        $s =~ s/\n//mog;
-        syslog('LOG_INFO', "OILS: Checkin response: $s");
-    }
+    while(1) {
 
-    # In oddball cases, we can receive an array of events.
-    # The first event received should be treated as the main result.
-    $resp = $$resp[0] if ref($resp) eq 'ARRAY';
+        my $method = 'open-ils.circ.checkin';
+        $method .= '.override' if $override;
 
-    my $code = $U->event_code($resp);
-    my $txt  = (defined $code) ? $resp->{textcode} : '';
+        $resp = $U->simplereq('open-ils.circ', $method, $self->{authtoken}, $args);
 
+        if ($debug) {
+            my $s = Dumper($resp);
+            $s =~ s/\n//mog;
+            syslog('LOG_INFO', "OILS: Checkin response: $s");
+        }
+
+        # In oddball cases, we can receive an array of events.
+        # The first event received should be treated as the main result.
+        $resp = $$resp[0] if ref($resp) eq 'ARRAY';
+        $code = $U->event_code($resp);
+        $txt  = (defined $code) ? $resp->{textcode} : '';
+
+        last if $override;
+
+        if ( $override_events{$txt} ) {
+            $override = 1;
+        } else {
+            last;
+        }
+    }
+
     syslog('LOG_INFO', "OILS: Checkin resulted in event: $txt, phys_location: $phys_location");
 
     $resp->{org} &&= OpenILS::SIP::shortname_from_id($resp->{org}); # Convert id to shortname
@@ -119,13 +150,21 @@
     
     my $payload = $resp->{payload} || {};
 
-    # Two places to look for hold data.  These are more important and more definitive than above.
-    if ($payload->{remote_hold}) {
-        # actually only used for checkin at non-owning branch w/ hold at same branch
-        $self->item->hold($payload->{remote_hold});     
+    my ($circ, $copy);
 
-    } elsif ($payload->{hold}) {
-        $self->item->hold($payload->{hold});
+    if(ref $payload eq 'HASH') {
+
+        # Two places to look for hold data.  These are more important and more definitive than above.
+        if ($payload->{remote_hold}) {
+            # actually only used for checkin at non-owning branch w/ hold at same branch
+            $self->item->hold($payload->{remote_hold});     
+
+        } elsif ($payload->{hold}) {
+            $self->item->hold($payload->{hold});
+        }
+
+        $circ = $resp->{payload}->{circ} || '';
+        $copy = $resp->{payload}->{copy} || '';
     }
 
     if ($self->item->hold) {
@@ -142,10 +181,8 @@
 
     $self->alert(1) if defined $self->alert_type;  # alert_type could be "00", hypothetically
 
-    my $circ = $resp->{payload}->{circ} || '';
-    my $copy = $resp->{payload}->{copy} || '';
-
     if ( $circ ) {
+        $self->{circ_user_id} = $circ->usr;
         $self->ok(1);
     } elsif ($txt eq 'NO_CHANGE' or $txt eq 'SUCCESS' or $txt eq 'ROUTE_ITEM') {
         $self->ok(1); # NO_CHANGE means it wasn't checked out anyway, no problem

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/SIP.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -381,7 +381,7 @@
 	$xact->do_checkin( $self, $inst_id, $trans_date, $return_date, $current_loc, $item_props );
 	
 	if ($xact->ok) {
-        $xact->patron($self->find_patron($item->{patron}));
+        $xact->patron($self->find_patron(usr => $xact->{circ_user_id})) if $xact->{circ_user_id};
         delete $item->{patron};
         delete $item->{due_date};
         syslog('LOG_INFO', "OILS: Checkin succeeded");

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -76,6 +76,11 @@
 	return $self;
 }
 
+sub DESTROY {
+        my $self = shift;
+        $self->reset;
+        return undef;
+}
 
 sub app {
 	my( $self, $app ) = @_;
@@ -289,8 +294,17 @@
 # -----------------------------------------------------------------------------
 sub rollback {
 	my $self = shift;
-	$self->xact_rollback;
-	$self->disconnect;
+    my $err;
+    my $ret;
+	try {
+        $self->xact_rollback;
+    } catch Error with  {
+        $err = shift
+    } finally {
+        $ret = $self->disconnect
+    };
+    throw $err if ($err);
+    return $ret;
 }
 
 sub disconnect {
@@ -329,8 +343,17 @@
 # -----------------------------------------------------------------------------
 sub finish {
 	my $self = shift;
-	$self->commit;
-	$self->reset;
+    my $err;
+    my $ret;
+	try {
+        $self->commit;
+    } catch Error with  {
+        $err = shift
+    } finally {
+        $ret = $self->reset
+    };
+    throw $err if ($err);
+    return $ret;
 }
 
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/Penalty.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/Penalty.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/Penalty.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -26,41 +26,53 @@
     my $penalties = $e->json_query({from => ['actor.calculate_system_penalties',$user_id, $context_org]});
 
     my $user = $e->retrieve_actor_user( $user_id );
-    my $ses = OpenSRF::AppSession->create('open-ils.trigger') if (@$penalties);
+    my @existing_penalties = grep { defined $_->{id} } @$penalties;
+    my @wanted_penalties = grep { !defined $_->{id} } @$penalties;
+    my @trigger_events;
 
     my %csp;
-    for my $pen_obj (@$penalties) {
+    for my $pen_obj (@wanted_penalties) {
 
-        next if grep { # leave duplicate penalties in place
-            $_->{org_unit} == $pen_obj->{org_unit} and
-            $_->{standing_penalty} == $pen_obj->{standing_penalty} and
-            ($_->{id} || '') ne ($pen_obj->{id} || '') } @$penalties;
-
         my $pen = Fieldmapper::actor::user_standing_penalty->new;
         $pen->$_($pen_obj->{$_}) for keys %$pen_obj;
 
-        if(defined $pen_obj->{id}) {
-            $e->delete_actor_user_standing_penalty($pen) or return $e->die_event;
+        # let's see if this penalty is accounted for already
+        my ($existing) = grep { 
+                $_->{org_unit} == $pen_obj->{org_unit} and
+                $_->{standing_penalty} == $pen_obj->{standing_penalty}
+            } @existing_penalties;
 
+        if($existing) { 
+            # we have one of these already.  Leave it be, but remove it from the 
+            # existing set so it's not deleted in the subsequent loop
+            @existing_penalties = grep { $_->{id} ne $existing->{id} }  @existing_penalties;
+
         } else {
+
+            # this is a new penalty
             $e->create_actor_user_standing_penalty($pen) or return $e->die_event;
 
-            my $csp_obj = $csp{$pen->standing_penalty} ||
+            my $csp_obj = $csp{$pen->standing_penalty} || 
                 $e->retrieve_config_standing_penalty( $pen->standing_penalty );
 
             # cache for later
             $csp{$pen->standing_penalty} = $csp_obj;
 
-            $ses->request(
-                'open-ils.trigger.event.autocreate',
-                'penalty.' . $csp_obj->name,
-                $pen,
-                $pen->org_unit
-            );
+            push(@trigger_events, ['penalty.' . $csp_obj->name, $pen, $pen->org_unit]);
         }
     }
 
+    # at this point, any penalties remaining in the existing 
+    # penalty set are unaccounted for and should be removed
+    for my $pen_obj (@existing_penalties) {
+        my $pen = Fieldmapper::actor::user_standing_penalty->new;
+        $pen->$_($pen_obj->{$_}) for keys %$pen_obj;
+        $e->delete_actor_user_standing_penalty($pen) or return $e->die_event;
+    }
+
     $e->commit if $commit;
+
+    $U->create_events_for_hook($$_[0], $$_[1], $$_[2]) for @trigger_events;
     return undef;
 }
 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/PermitHold.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/PermitHold.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/PermitHold.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -204,6 +204,7 @@
 sub indb_hold_permit {
     my $params = shift;
 
+    my $function = $$params{retarget} ? 'action.hold_retarget_permit_test' : 'action.hold_request_permit_test';
     my $patron_id = 
         ref($$params{patron}) ? $$params{patron}->id : $$params{patron_id};
     my $request_lib = 
@@ -211,7 +212,7 @@
 
     my $HOLD_TEST = {
         from => [
-            'action.hold_request_permit_test',
+            $function,
             $$params{pickup_lib}, 
             $request_lib,
             $$params{copy}->id, 

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/RemoteAccount.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/RemoteAccount.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/Utils/RemoteAccount.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1,4 +1,4 @@
-package   OpenILS::Utils::RemoteAccount;
+package OpenILS::Utils::RemoteAccount;
 
 # use OpenSRF::Utils::SettingsClient;
 use OpenSRF::Utils::Logger qw/:logger/;
@@ -45,12 +45,18 @@
 );
 
 
-=pod 
+=head1 NAME 
 
+OpenILS::Utils::RemoteAccount - Encapsulate FTP, SFTP and SSH file transactions for Evergreen
+
+=head1 DESCRIPTION
+
 The Remote Account module attempts to transfer a file to/from a remote server.
 Net::uFTP is used, encapsulating the available options of SCP, FTP and SFTP.
 
-All information is expected to be gathered by the Event Definition through event parameters:
+=head1 PARAMETERS
+
+All information is expected to be supplied by the caller via parameters:
    ~ remote_host (required)
    ~ remote_user
    ~ remote_password
@@ -79,15 +85,16 @@
 Similarly, specifying an account requires both user and password.
 That is, there are no assumed defaults when the latter arguments are used.
 
-SSH KEYS:
+=head2 SSH KEYS:
 
-The use of ssh keys is preferred. 
+The use of ssh keys is preferred.  Explicit specification of connection type will prevent
+multiple attempts to the same server.  Therefore, using the type parameter is also recommended.
 
-We attempt to use SSH keys where they are specified or otherwise found
+If the type is not explicit, we attempt to use SSH keys where they are specified or otherwise found
 in the runtime environment.  If only one key is specified, we attempt to derive
 the corresponding filename based on the ssh-keygen defaults.  If either key is
 specified, but both are not found (and readable) then the result is failure.  If
-no key is specified, but keys are found, the key-based connections will be attempted,
+no key or type is specified, but keys are found, the key-based connections will be attempted,
 but failure will be non-fatal.
 
 =cut

Modified: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/Proxy.pm
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/Proxy.pm	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/Proxy.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -80,7 +80,7 @@
         my $base = $cgi->url(-base=>1);
 		$base =~ s/^http:/https:/o;
 		print "Location: $base".$apache->unparsed_uri."\n\n";
-		return Apache2::Const::OK;
+		return Apache2::Const::REDIRECT;
 	}
 
 	if (!$auth_ses) {

Copied: branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm (from rev 18366, trunk/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm)
===================================================================
--- branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/perlmods/OpenILS/WWW/TemplateBatchBibUpdate.pm	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,671 @@
+package OpenILS::WWW::TemplateBatchBibUpdate;
+use strict;
+use warnings;
+use bytes;
+
+use Apache2::Log;
+use Apache2::Const -compile => qw(OK REDIRECT DECLINED NOT_FOUND :log);
+use APR::Const    -compile => qw(:error SUCCESS);
+use APR::Table;
+
+use Apache2::RequestRec ();
+use Apache2::RequestIO ();
+use Apache2::RequestUtil;
+use CGI;
+use Data::Dumper;
+use Text::CSV;
+
+use OpenSRF::EX qw(:try);
+use OpenSRF::Utils qw/:datetime/;
+use OpenSRF::Utils::Cache;
+use OpenSRF::System;
+use OpenSRF::AppSession;
+use XML::LibXML;
+use XML::LibXSLT;
+
+use Encode;
+use Unicode::Normalize;
+use OpenILS::Utils::Fieldmapper;
+use OpenSRF::Utils::Logger qw/$logger/;
+
+use MARC::Record;
+use MARC::File::XML;
+
+use UNIVERSAL::require;
+
+our @formats = qw/USMARC UNIMARC XML BRE/;
+
+# set the bootstrap config and template include directory when
+# this module is loaded
+my $bootstrap;
+
+sub import {
+    my $self = shift;
+    $bootstrap = shift;
+}
+
+
+sub child_init {
+    OpenSRF::System->bootstrap_client( config_file => $bootstrap );
+    Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
+}
+
+sub handler {
+    my $r = shift;
+    my $cgi = new CGI;
+
+    my $authid = $cgi->cookie('ses') || $cgi->param('ses');
+    my $usr = verify_login($authid);
+    return show_template($r) unless ($usr);
+
+    my $template = $cgi->param('template');
+    return show_template($r) unless ($template);
+
+
+    my $rsource = $cgi->param('recordSource');
+    # find some IDs ...
+    my @records;
+
+    if ($rsource eq 'r') {
+        @records = map { $_ ? ($_) : () } $cgi->param('recid');
+    }
+
+    if ($rsource eq 'c') { # try for a file
+        my $file = $cgi->param('idfile');
+        if ($file) {
+            my $col = $cgi->param('idcolumn') || 0;
+            my $csv = new Text::CSV;
+
+            while (<$file>) {
+                $csv->parse($_);
+                my @data = $csv->fields;
+                my $id = $data[$col];
+                $id =~ s/\D+//o;
+                next unless ($id);
+                push @records, $id;
+            }
+        }
+    }
+
+    my $e = OpenSRF::AppSession->connect('open-ils.cstore');
+    $e->request('open-ils.cstore.transaction.begin')->gather(1);
+
+    # still no records ...
+    my $container = $cgi->param('containerid');
+    if ($rsource eq 'b') {
+        if ($container) {
+            my $bucket = $e->request(
+                'open-ils.cstore.direct.container.biblio_record_entry_bucket.retrieve',
+                $container
+            )->gather(1);
+            unless($bucket) {
+                $e->request('open-ils.cstore.transaction.rollback')->gather(1);
+                $e->disconnect;
+                $r->log->error("No such bucket $container");
+                $logger->error("No such bucket $container");
+                return Apache2::Const::NOT_FOUND;
+            }
+            my $recs = $e->request(
+                'open-ils.cstore.direct.container.biblio_record_entry_bucket_item.search.atomic',
+                { bucket => $container }
+            )->gather(1);
+            @records = map { ($_->target_biblio_record_entry) } @$recs;
+        }
+    }
+
+    unless (@records) {
+        $e->request('open-ils.cstore.transaction.rollback')->gather(1);
+        $e->disconnect;
+        return show_template($r);
+    }
+
+    # we have a template and some record ids, so...
+
+    # insert the template record
+    my $min_id = $e->request(
+        'open-ils.cstore.json_query',
+        { select => { bre => [{ column => 'id', transform => 'min', aggregate => 1}] }, from => 'bre' }
+    )->gather(1)->{id} - 1;
+
+    warn "new template bib id = $min_id\n";
+
+    my $tmpl_rec = Fieldmapper::biblio::record_entry->new;
+    $tmpl_rec->id($min_id);
+    $tmpl_rec->deleted('t');
+    $tmpl_rec->active('f');
+    $tmpl_rec->marc($template);
+    $tmpl_rec->creator($usr->id);
+    $tmpl_rec->editor($usr->id);
+
+    warn "about to create bib $min_id\n";
+    $e->request('open-ils.cstore.direct.biblio.record_entry.create', $tmpl_rec )->gather(1);
+
+    # create the new container for the records and the template
+    my $bucket = Fieldmapper::container::biblio_record_entry_bucket->new;
+    $bucket->owner($usr->id);
+    $bucket->btype('template_merge');
+
+    my $bname = $cgi->param('bname') || 'Temporary Merge Bucket '. localtime() . ' ' . $usr->id;
+    $bucket->name($bname);
+
+    $bucket = $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket.create', $bucket )->gather(1);
+
+    # create items in the bucket
+    my $item = Fieldmapper::container::biblio_record_entry_bucket_item->new;
+    $item->bucket($bucket->id);
+    $item->target_biblio_record_entry($min_id);
+
+    $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket_item.create', $item )->gather(1);
+
+    my %seen;
+    for my $r (@records) {
+        next if ($seen{$r});
+        $item->target_biblio_record_entry($r);
+        $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket_item.create', $item )->gather(1);
+        $seen{$r}++;
+    }
+
+    $e->request('open-ils.cstore.transaction.commit')->gather(1);
+    $e->disconnect;
+
+    # fire the background bucket processor
+    my $cache_key = OpenSRF::AppSession
+        ->create('open-ils.cat')
+        ->request('open-ils.cat.container.template_overlay.background', $authid, $bucket->id)
+        ->gather(1);
+
+    return show_processing_template($r, $bucket->id, \@records, $cache_key);
+}
+
+sub verify_login {
+        my $auth_token = shift;
+        return undef unless $auth_token;
+
+        my $user = OpenSRF::AppSession
+                ->create("open-ils.auth")
+                ->request( "open-ils.auth.session.retrieve", $auth_token )
+                ->gather(1);
+
+        if (ref($user) eq 'HASH' && $user->{ilsevent} == 1001) {
+                return undef;
+        }
+
+        return $user if ref($user);
+        return undef;
+}
+
+sub show_processing_template {
+    my $r = shift;
+    my $bid = shift;
+    my $recs = shift;
+    my $cache_key = shift;
+
+    my $rec_string = @$recs;
+
+    $r->content_type('text/html');
+    $r->print(<<HTML);
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+    <head>
+        <title>Merging records...</title>
+        <style type="text/css">
+            \@import '/js/dojo/dojo/resources/dojo.css';
+            \@import '/js/dojo/dijit/themes/tundra/tundra.css';
+            .hide_me { display: none; visibility: hidden; }
+            th       { font-weight: bold; }
+        </style>
+
+        <script type="text/javascript">
+            var djConfig= {
+                isDebug: false,
+                parseOnLoad: true,
+                AutoIDL: ['aou','aout','pgt','au','cbreb']
+            }
+        </script>
+
+        <script src='/js/dojo/dojo/dojo.js'></script>
+        <!-- <script src="/js/dojo/dojo/openils_dojo.js"></script> -->
+
+        <script type="text/javascript">
+
+            dojo.require('fieldmapper.AutoIDL');
+            dojo.require('fieldmapper.dojoData');
+            dojo.require('openils.User');
+            dojo.require('openils.CGI');
+            dojo.require('openils.widget.ProgressDialog');
+
+            var cgi = new openils.CGI();
+            var u = new openils.User({ authcookie : 'ses' });
+
+            dojo.addOnLoad(function () {
+                progress_dialog.show(true);
+                progress_dialog.update({maximum:$rec_string});
+
+                var interval;
+                interval = setInterval( function() {
+                    fieldmapper.standardRequest(
+                        ['open-ils.actor','open-ils.actor.anon_cache.get_value'],
+                        { async : false,
+                          params: [ u.authtoken, 'res_list' ],
+                          onerror : function (r) { progress_dialog.hide(); },
+                          onresponse : function (r) {
+                            var counter = { success : 0, fail : 0, total : 0 };
+                            dojo.forEach( openils.Util.readResponse(r), function(x) {
+                                if (x.complete) {
+                                    clearInterval(interval);
+                                    progress_dialog.hide();
+                                    if (x.success == 't') dojo.byId('complete_msg').innerHTML = 'Overlay completed successfully';
+                                    else dojo.byId('complete_msg').innerHTML = 'Overlay did not complet successfully';
+                                } else {
+                                    counter.total++;
+                                    switch (x.success) {
+                                        case 't':
+                                            counter.success++;
+                                            break;
+                                        default:
+                                            counter.fail++;
+                                            break;
+                                    }
+                                }
+                            });
+
+                            // update the progress dialog
+                            progress_dialog.update({progress:counter.total});
+                            dojo.byId('success_count').innerHTML = counter.success;
+                            dojo.byId('fail_count').innerHTML = counter.fail;
+                            dojo.byId('total_count').innerHTML = counter.total;
+                          }
+                        }
+                    );
+                }, 1000);
+
+            });
+        </script>
+    </head>
+
+    <body style="margin:10px;" class='tundra'>
+        <div class="hide_me"><div dojoType="openils.widget.ProgressDialog" jsId="progress_dialog"></div></div>
+
+        <table style="width:100%; margin-top:100px;">
+            <th>
+                <td>Status</td>
+                <td>Record Count</td>
+            </th>
+            <tr>
+                <td>Success</td>
+                <td id='success_count'></td>
+            </tr>
+            <tr>
+                <td>Failure</td>
+                <td id='fail_count'></td>
+            </tr>
+            <tr>
+                <td></td>
+                <td id='total_count'></td>
+            </tr>
+        </table>
+
+        <div id='complete_msg'></div>
+
+    </body>
+</html>
+HTML
+
+    return Apache2::Const::OK;
+}
+
+
+sub show_template {
+    my $r = shift;
+
+    $r->content_type('text/html');
+    $r->print(<<'HTML');
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+    <head>
+        <title>Merge Template Builder</title>
+        <style type="text/css">
+            @import '/js/dojo/dojo/resources/dojo.css';
+            @import '/js/dojo/dijit/themes/tundra/tundra.css';
+            .hide_me { display: none; visibility: hidden; }
+            table.ruleTable th { padding: 5px; border-collapse: collapse; border-bottom: solid 1px gray; font-weight: bold; }
+            table.ruleTable td { padding: 5px; border-collapse: collapse; border-bottom: solid 1px gray; }
+        </style>
+
+        <script type="text/javascript">
+            var djConfig= {
+                isDebug: false,
+                parseOnLoad: true,
+                AutoIDL: ['aou','aout','pgt','au','cbreb']
+            }
+        </script>
+
+        <script src='/js/dojo/dojo/dojo.js'></script>
+        <!-- <script src="/js/dojo/dojo/openils_dojo.js"></script> -->
+
+        <script type="text/javascript">
+
+            dojo.require('dojo.data.ItemFileReadStore');
+            dojo.require('dijit.form.Form');
+            dojo.require('dijit.form.NumberSpinner');
+            dojo.require('dijit.form.FilteringSelect');
+            dojo.require('dijit.form.TextBox');
+            dojo.require('dijit.form.Textarea');
+            dojo.require('dijit.form.Button');
+            dojo.require('MARC.Batch');
+            dojo.require('fieldmapper.AutoIDL');
+            dojo.require('fieldmapper.dojoData');
+            dojo.require('openils.User');
+            dojo.require('openils.CGI');
+
+            var cgi = new openils.CGI();
+            var u = new openils.User({ authcookie : 'ses' });
+
+            var bucketStore = new dojo.data.ItemFileReadStore(
+                { data : cbreb.toStoreData(
+                        fieldmapper.standardRequest(
+                            ['open-ils.actor','open-ils.actor.container.retrieve_by_class'],
+                            [u.authtoken, u.user.id(), 'biblio', 'staff_client']
+                        )
+                    )
+                }
+            );
+
+            function render_preview () {
+                var rec = ruleset_to_record();
+                dojo.byId('marcPreview').innerHTML = rec.toBreaker();
+            }
+
+            function render_from_template () {
+                var kid_number = dojo.byId('ruleList').childNodes.length;
+                var clone = dojo.query('*[name=ruleTable]', dojo.byId('ruleTemplate'))[0].cloneNode(true);
+
+                var typeSelect = dojo.query('*[name=typeSelect]',clone).instantiate(dijit.form.FilteringSelect, {
+                    onChange : function (val) {
+                        switch (val) {
+                            case 'a':
+                            case 'r':
+                                dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]).attr('disabled',false);
+                                break;
+                            default :
+                                dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]).attr('disabled',true);
+                        };
+                        render_preview();
+                    }
+                })[0];
+
+                var marcData = dojo.query('*[name=marcData]',clone).instantiate(dijit.form.TextBox, {
+                    onChange : render_preview
+                })[0];
+
+
+                var tag = dojo.query('*[name=tag]',clone).instantiate(dijit.form.TextBox, {
+                    onChange : function (newtag) {
+                        var md = dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]);
+                        var current_marc = md.attr('value');
+
+                        if (newtag.length == 3) {
+                            if (current_marc.length == 0) newtag += ' \\\\';
+                            if (current_marc.substr(0,3) != newtag) current_marc = newtag + current_marc.substr(3);
+                        }
+                        md.attr('value', current_marc);
+                        render_preview();
+                    }
+                })[0];
+
+                var sf = dojo.query('*[name=sf]',clone).instantiate(dijit.form.TextBox, {
+                    onChange : function (newsf) {
+                        var md = dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]);
+                        var current_marc = md.attr('value');
+                        var sf_list = newsf.split('');
+
+                        for (var i in sf_list) {
+                            var re = '\\$' + sf_list[i];
+                            if (current_marc.match(re)) continue;
+                            current_marc += '$' + sf_list[i];
+                        }
+
+                        md.attr('value', current_marc);
+                        render_preview();
+                    }
+                })[0];
+
+                var matchSF = dojo.query('*[name=matchSF]',clone).instantiate(dijit.form.TextBox, {
+                    onChange : render_preview
+                })[0];
+
+                var matchRE = dojo.query('*[name=matchRE]',clone).instantiate(dijit.form.TextBox, {
+                    onChange : render_preview
+                })[0];
+
+                var removeButton = dojo.query('*[name=removeButton]',clone).instantiate(dijit.form.Button, {
+                    onClick : function() {
+                        dojo.addClass(
+                            dojo.byId('ruleList').childNodes[kid_number],
+                            'hide_me'
+                        );
+                        render_preview();
+                    }
+                })[0];
+
+                dojo.place(clone,'ruleList');
+            }
+
+            function ruleset_to_record () {
+                var rec = new MARC.Record ({ delimiter : '$' });
+
+                dojo.forEach( 
+                    dojo.query('#ruleList *[name=ruleTable]').filter( function (node) {
+                        if (node.className.match(/hide_me/)) return false;
+                        return true;
+                    }),
+                    function (tbl) {
+                        var rule_tag = new MARC.Field ({
+                            tag : '905',
+                            ind1 : ' ',
+                            ind2 : ' '
+                        });
+                        var rule_txt = dijit.byNode(dojo.query('*[name=tagContainer] .dijit',tbl)[0]).attr('value');
+                        rule_txt += dijit.byNode(dojo.query('*[name=sfContainer] .dijit',tbl)[0]).attr('value');
+
+                        var reSF = dijit.byNode(dojo.query('*[name=matchSFContainer] .dijit',tbl)[0]).attr('value');
+                        if (reSF) {
+                            var reRE = dijit.byNode(dojo.query('*[name=matchREContainer] .dijit',tbl)[0]).attr('value');
+                            rule_txt += '[' + reSF + '~' + reRE + ']';
+                        }
+
+                        var rtype = dijit.byNode(dojo.query('*[name=typeSelectContainer] .dijit',tbl)[0]).attr('value');
+                        rule_tag.addSubfields( rtype, rule_txt )
+                        rec.appendFields( rule_tag );
+
+                        if (rtype == 'a' || rtype == 'r') {
+                            rec.appendFields(
+                                new MARC.Record ({
+                                    delimiter : '$',
+                                    marcbreaker : dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',tbl)[0]).attr('value')
+                                }).fields[0]
+                            );
+                        }
+                    }
+                );
+
+                return rec;
+            }
+        </script>
+    </head>
+
+    <body style="margin:10px;" class='tundra'>
+
+        <div dojoType="dijit.form.Form" id="myForm" jsId="myForm" encType="multipart/form-data" action="" method="POST">
+                <script type='dojo/method' event='onSubmit'>
+                    var rec = ruleset_to_record();
+
+                    if (rec.subfield('905','r') == '') { // no-op to force replace mode
+                        rec.appendFields(
+                            new MARC.Field ({
+                                tag : '905',
+                                ind1 : ' ',
+                                ind2 : ' ',
+                                subfields : [['r','901c']]
+                            })
+                        );
+                    }
+
+                    dojo.byId('template_value').value = rec.toXmlString();
+                    return true;
+                </script>
+
+            <input type='hidden' id='template_value' name='template'/>
+
+            <label for='inputTypeSelect'>Record source:</label>
+            <select name='recordSource' dojoType='dijit.form.FilteringSelect'>
+                <script type='dojo/method' event='onChange' args="val">
+                    switch (val) {
+                        case 'b':
+                            dojo.removeClass('bucketListContainer','hide_me');
+                            dojo.addClass('csvContainer','hide_me');
+                            dojo.addClass('recordContainer','hide_me');
+                            break;
+                        case 'c':
+                            dojo.addClass('bucketListContainer','hide_me');
+                            dojo.removeClass('csvContainer','hide_me');
+                            dojo.addClass('recordContainer','hide_me');
+                            break;
+                        case 'r':
+                            dojo.addClass('bucketListContainer','hide_me');
+                            dojo.addClass('csvContainer','hide_me');
+                            dojo.removeClass('recordContainer','hide_me');
+                            break;
+                    };
+                </script>
+                <script type='dojo/method' event='postCreate'>
+                    if (cgi.param('recordSource')) {
+                        this.attr('value',cgi.param('recordSource'));
+                        this.onChange(cgi.param('recordSource'));
+                    }
+                </script>
+                <option value='b'>a Bucket</option>
+                <option value='c'>a CSV File</option>
+                <option value='r'>a specific record ID</option>
+            </select>
+
+            <table style='margin:10px; margin-bottom:20px;'>
+<!--
+                <tr>
+                    <th>Merge template name (optional):</th>
+                    <td><input id='bucketName' jsId='bucketName' type='text' dojoType='dijit.form.TextBox' name='bname' value=''/></td>
+                </tr>
+-->
+                <tr class='' id='bucketListContainer'>
+                    <td>Bucket named: 
+                        <div name='containerid' jsId='bucketList' dojoType='dijit.form.FilteringSelect' store='bucketStore' searchAttr='name' id='bucketList'>
+                            <script type='dojo/method' event='postCreate'>
+                                if (cgi.param('containerid')) this.attr('value',cgi.param('containerid'));
+                            </script>
+                        </div>
+                    </td>
+                </tr>
+                <tr class='hide_me' id='csvContainer'>
+                    <td>
+                        Column <input style='width:75px;' type='text' dojoType='dijit.form.NumberSpinner' name='idcolumn' value='0' constraints='{min:0,max:100,places:0}' /> of: 
+                        <input id='idfile' type="file" name="idfile"/>
+                        <br/>
+                        <br/>
+                        Columns are numbered starting at 0.  For instance, when looking at a CSV file in Excel, the column labeled A is the same as column 0, and the column labeled B is the same as column 1.
+                    </td>
+                </tr>
+                <tr class='hide_me' id='recordContainer'>
+                    <td>Record ID: <input dojoType='dijit.form.TextBox' name='recid' style='width:75px;' type='text' value=''/></td>
+                </tr>
+            </table>
+
+            <button type="submit" dojoType='dijit.form.Button'>GO!</button> (After setting up your template below.)
+
+            <br/>
+            <br/>
+
+        </div> <!-- end of the form -->
+
+        <hr/>
+        <table style='width: 100%'>
+            <tr>
+                <td style='width: 50%'><div id='ruleList'></div></td>
+                <td valign='top'>Update Template Preview:<br/><pre id='marcPreview'></pre></td>
+            </tr>
+        </table>
+
+        <button dojoType='dijit.form.Button'>Add Merge Rule
+            <script type='dojo/connect' event='onClick'>render_from_template()</script>
+            <script type='dojo/method' event='postCreate'>render_from_template()</script>
+        </button>
+
+        <div class='hide_me' id='ruleTemplate'>
+        <div name='ruleTable'>
+            <table class='ruleTable'>
+                <tbody>
+                    <tr>
+                        <th style="text-align:center;">Rule Setup</th>
+                        <th style="text-align:center;">Data</th>
+                        <th style="text-align:center;">Help</th>
+                    </tr>
+                    <tr>
+                        <th>Action (Rule Type)</th>
+                        <td name='typeSelectContainer'>
+                            <select name='typeSelect'>
+                                <option value='r'>Replace</option>
+                                <option value='a'>Add</option>
+                                <option value='d'>Delete</option>
+                            </select>
+                        </td>
+                        <td>How to change the existing records</td>
+                    </tr>
+                    <tr>
+                        <th>MARC Tag</th>
+                        <td name='tagContainer'><input style='with: 2em;' name='tag' type='text'></input</td>
+                        <td>Three characters, no spaces, no indicators, etc. eg: 245</td>
+                    </td>
+                    <tr>
+                        <th>Subfields (optional)</th>
+                        <td name='sfContainer'><input name='sf' type='text'/></td>
+                        <td>No spaces, no delimiters, eg: abcnp</td>
+                    </tr>
+                    <tr>
+                        <th>MARC Data</th>
+                        <td name='marcDataContainer'><input name='marcData' type='text'/></td>
+                        <td>MARC-Breaker formatted data with indicators and subfield delimiters, eg:<br/>245 04$aThe End</td>
+                    </tr>
+                    <tr>
+                        <th colspan='3' style='padding-top: 20px; text-align: center;'>Advanced Matching Restriction (Optional)</th>
+                    </tr>
+                    <tr>
+                        <th>Subfield</th>
+                        <td name='matchSFContainer'><input style='with: 2em;' name='matchSF' type='text'></input</td>
+                        <td>A single subfield code, no delimiters, eg: a</td>
+                    <tr>
+                        <th>Regular Expression</th>
+                        <td name='matchREContainer'><input name='matchRE' type='text'/></td>
+                        <td>See <a href="http://perldoc.perl.org/perlre.html#Regular-Expressions" target="_blank">the Perl documentation</a>
+                            for an explanation of Regular Expressions.
+                        </td>
+                    </tr>
+                    <tr>
+                        <td colspan='3' style='padding-top: 20px; text-align: center;'>
+                            <button name='removeButton'>Remove this Template Rule</button>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+        <hr/>
+        </div>
+        </div>
+
+    </body>
+</html>
+HTML
+
+    return Apache2::Const::OK;
+}
+
+1;
+
+

Modified: branches/serials-integration/Open-ILS/src/reporter/clark-kent.pl
===================================================================
--- branches/serials-integration/Open-ILS/src/reporter/clark-kent.pl	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/reporter/clark-kent.pl	2010-10-18 14:05:06 UTC (rev 18372)
@@ -423,6 +423,8 @@
 	$sheetname =~ s/\W/_/gos;
 	
 	my $sheet = $xls->add_worksheet($sheetname);
+	# don't try to write formulas, just write anything that starts with = as a text cell
+	$sheet->add_write_handler(qr/^=/, sub { return shift->write_string(@_); } );
 
 	$sheet->write_row('A1', $r->{column_labels});
 

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/002.functions.config.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/002.functions.config.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/002.functions.config.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -272,33 +272,37 @@
     select_list := ARRAY_APPEND( select_list, key || '::INT AS key' );
 
     FOR i IN 1 .. ARRAY_UPPER(xpath_list,1) LOOP
-        select_list := ARRAY_APPEND(
-            select_list,
-            $sel$
-            EXPLODE_ARRAY(
-                COALESCE(
-                    NULLIF(
-                        oils_xpath(
-                            $sel$ ||
-                                quote_literal(
-                                    CASE
-                                        WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
-                                        ELSE xpath_list[i] || '//text()'
-                                    END
-                                ) ||
-                            $sel$,
-                            $sel$ || document_field || $sel$
+        IF xpath_list[i] = 'null()' THEN
+            select_list := ARRAY_APPEND( select_list, 'NULL::TEXT AS c_' || i );
+        ELSE
+            select_list := ARRAY_APPEND(
+                select_list,
+                $sel$
+                EXPLODE_ARRAY(
+                    COALESCE(
+                        NULLIF(
+                            oils_xpath(
+                                $sel$ ||
+                                    quote_literal(
+                                        CASE
+                                            WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
+                                            ELSE xpath_list[i] || '//text()'
+                                        END
+                                    ) ||
+                                $sel$,
+                                $sel$ || document_field || $sel$
+                            ),
+                           '{}'::TEXT[]
                         ),
-                       '{}'::TEXT[]
-                    ),
-                    '{NULL}'::TEXT[]
-                )
-            ) AS c_$sel$ || i
-        );
-        where_list := ARRAY_APPEND(
-            where_list,
-            'c_' || i || ' IS NOT NULL'
-        );
+                        '{NULL}'::TEXT[]
+                    )
+                ) AS c_$sel$ || i
+            );
+            where_list := ARRAY_APPEND(
+                where_list,
+                'c_' || i || ' IS NOT NULL'
+            );
+        END IF;
     END LOOP;
 
     q := $q$
@@ -593,5 +597,9 @@
 return;
 $func$ LANGUAGE PLPERLU;
 
+CREATE OR REPLACE FUNCTION oils_text_as_bytea (TEXT) RETURNS BYTEA AS $_$
+    SELECT CAST(REGEXP_REPLACE($1, $$\\$$, $$\\\\$$, 'g') AS BYTEA);
+$_$ LANGUAGE SQL IMMUTABLE;
+
 COMMIT;
 

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/002.schema.config.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/002.schema.config.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/002.schema.config.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -70,7 +70,7 @@
     install_date    TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
 );
 
-INSERT INTO config.upgrade_log (version) VALUES ('0412'); -- phasefx
+INSERT INTO config.upgrade_log (version) VALUES ('0439'); -- miker
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
@@ -316,7 +316,8 @@
 	extended	INTERVAL	NOT NULL,
 	normal		INTERVAL	NOT NULL,
 	shrt		INTERVAL	NOT NULL,
-	max_renewals	INT		NOT NULL
+	max_renewals	INT		NOT NULL,
+	date_ceiling    TIMESTAMPTZ
 );
 COMMENT ON TABLE config.rule_circ_duration IS $$
 /*
@@ -342,6 +343,14 @@
  */
 $$;
 
+CREATE TABLE config.hard_due_date (
+    id              SERIAL      PRIMARY KEY,
+    duration_rule   INT         NOT NULL REFERENCES config.rule_circ_duration (id)
+                                DEFERRABLE INITIALLY DEFERRED,
+    ceiling_date    TIMESTAMPTZ NOT NULL,
+    active_date     TIMESTAMPTZ NOT NULL
+);
+
 CREATE TABLE config.rule_max_fine (
     id          SERIAL          PRIMARY KEY,
     name        TEXT            NOT NULL UNIQUE CHECK ( name ~ E'^\\w+$' ),

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/006.schema.permissions.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/006.schema.permissions.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/006.schema.permissions.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -38,7 +38,8 @@
 	usergroup		BOOL	NOT NULL DEFAULT TRUE,
 	perm_interval		INTERVAL DEFAULT '3 years'::interval NOT NULL,
 	description		TEXT,
-	application_perm	TEXT
+	application_perm	TEXT,
+	hold_priority       INT   NOT NULL DEFAULT 0
 );
 CREATE INDEX grp_tree_parent_idx ON permission.grp_tree (parent);
 

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/012.schema.vandelay.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/012.schema.vandelay.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/012.schema.vandelay.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -77,7 +77,7 @@
 CREATE TABLE vandelay.queued_bib_record (
 	queue		INT		NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	bib_source	INT		REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
-	imported_as	INT		REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+	imported_as	BIGINT	REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
 ) INHERITS (vandelay.queued_record);
 ALTER TABLE vandelay.queued_bib_record ADD PRIMARY KEY (id);
 CREATE INDEX queued_bib_record_queue_idx ON vandelay.queued_bib_record (queue);
@@ -347,12 +347,18 @@
     for my $f ( keys %fields) {
         if ( @{$fields{$f}{sf}} ) {
             for my $from_field ($source_r->field( $f )) {
-                for my $to_field ($target_r->field( $f )) {
-                    if (exists($fields{$f}{match})) {
-                        next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
+                my @tos = $target_r->field( $f );
+                if (!@tos) {
+                    my @new_fields = map { $_->clone } $source_r->field( $f );
+                    $target_r->insert_fields_ordered( @new_fields );
+                } else {
+                    for my $to_field (@tos) {
+                        if (exists($fields{$f}{match})) {
+                            next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
+                        }
+                        my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}{sf}};
+                        $to_field->add_subfields( @new_sf );
                     }
-                    my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}{sf}};
-                    $to_field->add_subfields( @new_sf );
                 }
             }
         } else {
@@ -469,10 +475,10 @@
         END IF;
     END IF;
 
-    add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),''),'');
-    strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),''),'');
-    replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),''),'');
-    preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),''),'');
+    add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),','),'');
+    strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),','),'');
+    replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),','),'');
+    preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),','),'');
 
     output.add_rule := BTRIM(add_rule,',');
     output.replace_rule := BTRIM(replace_rule,',');

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/030.schema.metabib.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/030.schema.metabib.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/030.schema.metabib.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -729,8 +729,8 @@
                             JOIN config.marc21_physical_characteristic_subfield_map s ON (s.id = p.subfield)
                             JOIN config.marc21_physical_characteristic_value_map v ON (v.id = p.value)
                       WHERE p.ptype = 'v' AND s.subfield = 'e'    ),
-                biblio.marc21_extract_fixed_field( bib_id, 'Date1'),
-                biblio.marc21_extract_fixed_field( bib_id, 'Date2');
+                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date1'), ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
+                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date2'), ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
 
     RETURN;
 END;

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/040.schema.asset.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/040.schema.asset.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/040.schema.asset.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -293,8 +293,8 @@
 CREATE INDEX asset_call_number_creator_idx ON asset.call_number (creator);
 CREATE INDEX asset_call_number_editor_idx ON asset.call_number (editor);
 CREATE INDEX asset_call_number_dewey_idx ON asset.call_number (public.call_number_dewey(label));
-CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (upper(label),id,owning_lib);
-CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(label_sortkey);
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(upper(label)),id,owning_lib);
+CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(oils_text_as_bytea(label_sortkey));
 CREATE UNIQUE INDEX asset_call_number_label_once_per_lib ON asset.call_number (record, owning_lib, label) WHERE deleted = FALSE OR deleted IS FALSE;
 CREATE RULE protect_cn_delete AS ON DELETE TO asset.call_number DO INSTEAD UPDATE asset.call_number SET deleted = TRUE WHERE OLD.id = asset.call_number.id;
 CREATE TRIGGER asset_label_sortkey_trigger

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/070.schema.container.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/070.schema.container.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/070.schema.container.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -161,7 +161,7 @@
 							ON UPDATE CASCADE
 							DEFERRABLE
 							INITIALLY DEFERRED,
-	target_biblio_record_entry	INT	NOT NULL
+	target_biblio_record_entry	BIGINT	NOT NULL
 						REFERENCES biblio.record_entry (id)
 							ON DELETE CASCADE
 							ON UPDATE CASCADE

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/090.schema.action.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/090.schema.action.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/090.schema.action.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -146,6 +146,7 @@
 CREATE INDEX circ_all_usr_idx       ON action.circulation ( usr );
 CREATE INDEX circ_circ_staff_idx    ON action.circulation ( circ_staff );
 CREATE INDEX circ_checkin_staff_idx ON action.circulation ( checkin_staff );
+CREATE INDEX action_circulation_target_copy_idx ON action.circulation (target_copy);
 CREATE UNIQUE INDEX circ_parent_idx ON action.circulation ( parent_circ ) WHERE parent_circ IS NOT NULL;
 CREATE UNIQUE INDEX only_one_concurrent_checkout_per_copy ON action.circulation(target_copy) WHERE checkin_time IS NULL;
 
@@ -163,7 +164,7 @@
 END;
 $$ LANGUAGE PLPGSQL;
 
-CREATE TRIGGER push_due_date_tgr BEFORE INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
+CREATE TRIGGER push_due_date_tgr BEFORE INSERT OR UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
 
 CREATE TABLE action.aged_circulation (
 	usr_post_code		TEXT,
@@ -185,6 +186,7 @@
 CREATE INDEX aged_circ_copy_circ_lib_idx ON "action".aged_circulation (copy_circ_lib);
 CREATE INDEX aged_circ_copy_owning_lib_idx ON "action".aged_circulation (copy_owning_lib);
 CREATE INDEX aged_circ_copy_location_idx ON "action".aged_circulation (copy_location);
+CREATE INDEX action_aged_circulation_target_copy_idx ON action.aged_circulation (target_copy);
 
 CREATE OR REPLACE VIEW action.all_circulation AS
     SELECT  id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
@@ -409,7 +411,7 @@
 CREATE INDEX ahn_notify_staff_idx ON action.hold_notification ( notify_staff );
 
 CREATE TABLE action.hold_copy_map (
-	id		SERIAL	PRIMARY KEY,
+	id		BIGSERIAL	PRIMARY KEY,
 	hold		INT	NOT NULL REFERENCES action.hold_request (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
 	target_copy	BIGINT	NOT NULL, -- REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, -- XXX could be an serial.issuance
 	CONSTRAINT copy_once_per_hold UNIQUE (hold,target_copy)
@@ -776,7 +778,7 @@
             SELECT * INTO circ_chain_tail FROM action.circ_chain(circ_chain_head.id) ORDER BY xact_start DESC LIMIT 1;
             EXIT WHEN circ_chain_tail.xact_finish IS NULL;
 
-            -- Now get the user setings, if any, to block purging if the user wants to keep more circs
+            -- Now get the user settings, if any, to block purging if the user wants to keep more circs
             usr_keep_age.value := NULL;
             SELECT * INTO usr_keep_age FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_age';
 
@@ -792,7 +794,7 @@
             ELSIF usr_keep_start.value IS NOT NULL THEN
                 keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
             ELSE
-                keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTEVAL );
+                keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTERVAL );
             END IF;
 
             EXIT WHEN AGE(NOW(), circ_chain_tail.xact_finish) < keep_age;

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/095.schema.booking.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/095.schema.booking.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/095.schema.booking.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -32,9 +32,9 @@
 	                               DEFERRABLE INITIALLY DEFERRED,
 	catalog_item   BOOLEAN         NOT NULL DEFAULT FALSE,
 	transferable   BOOLEAN         NOT NULL DEFAULT FALSE,
-    record         INT             REFERENCES biblio.record_entry (id)
+    record         BIGINT          REFERENCES biblio.record_entry (id)
                                    DEFERRABLE INITIALLY DEFERRED,
-	CONSTRAINT brt_name_once_per_owner UNIQUE(owner, name, record)
+	CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record)
 );
 
 CREATE TABLE booking.resource (

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/1.6.1-2.0-upgrade-db.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1,24 +1,44 @@
+-- Before starting the transaction: drop some constraints that
+-- may or may not exist.
+
+DROP INDEX asset.asset_call_number_upper_label_id_owning_lib_idx;
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(upper(label)),id,owning_lib);
+
+
+\qecho Before starting the transaction: drop some constraints.
+\qecho If a DROP fails because the constraint doesn't exist, ignore the failure.
+
+ALTER TABLE permission.grp_perm_map        DROP CONSTRAINT grp_perm_map_perm_fkey;
+ALTER TABLE permission.usr_perm_map        DROP CONSTRAINT usr_perm_map_perm_fkey;
+ALTER TABLE permission.usr_object_perm_map DROP CONSTRAINT usr_object_perm_map_perm_fkey;
+ALTER TABLE booking.resource_type          DROP CONSTRAINT brt_name_or_record_once_per_owner;
+ALTER TABLE booking.resource_type          DROP CONSTRAINT brt_name_once_per_owner;
+
+\qecho Beginning the transaction now
+
 BEGIN;
 
--- Highest-numbered individual upgrade script
--- incorporated herein:
+-- Highest-numbered individual upgrade script incorporated herein:
 
-INSERT INTO config.upgrade_log (version) VALUES ('0403');
+INSERT INTO config.upgrade_log (version) VALUES ('0433');
 
--- Begin by upgrading permission.perm_list.  This is fairly complicated.
+-- Recreate one of the constraints that we just dropped,
+-- under a different name:
 
+ALTER TABLE booking.resource_type
+	ADD CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record);
+
+-- Now upgrade permission.perm_list.  This is fairly complicated.
+
 -- Add ON UPDATE CASCADE to some foreign keys so that, when we renumber the
 -- permissions, the dependents will follow and stay in sync:
 
-ALTER TABLE permission.grp_perm_map DROP CONSTRAINT grp_perm_map_perm_fkey;
 ALTER TABLE permission.grp_perm_map ADD CONSTRAINT grp_perm_map_perm_fkey FOREIGN KEY (perm)
     REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
 
-ALTER TABLE permission.usr_perm_map DROP CONSTRAINT usr_perm_map_perm_fkey;
 ALTER TABLE permission.usr_perm_map ADD CONSTRAINT usr_perm_map_perm_fkey FOREIGN KEY (perm)
     REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
 
-ALTER TABLE permission.usr_object_perm_map DROP CONSTRAINT usr_object_perm_map_perm_fkey;
 ALTER TABLE permission.usr_object_perm_map ADD CONSTRAINT usr_object_perm_map_perm_fkey FOREIGN KEY (perm)
     REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
 
@@ -885,99 +905,108 @@
      'View org unit settings related to credit card processing' );
 INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 390, 'ADMIN_CREDIT_CARD_PROCESSING',
      'Update org unit settings related to credit card processing' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 391, 'ADMIN_SERIAL_CAPTION_PATTERN',
+	'Create/update/delete serial caption and pattern objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 392, 'ADMIN_SERIAL_SUBSCRIPTION',
+	'Create/update/delete serial subscription objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 393, 'ADMIN_SERIAL_DISTRIBUTION',
+	'Create/update/delete serial distribution objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 394, 'ADMIN_SERIAL_STREAM',
+	'Create/update/delete serial stream objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 395, 'RECEIVE_SERIAL',
+	'Receive serial items' );
 
 -- Now for the permissions from the IDL.  We don't have descriptions for them.
 
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 391, 'ADMIN_ACQ_CLAIM' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 392, 'ADMIN_ACQ_CLAIM_EVENT_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 393, 'ADMIN_ACQ_CLAIM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 394, 'ADMIN_ACQ_DISTRIB_FORMULA' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 395, 'ADMIN_ACQ_FISCAL_YEAR' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 396, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 397, 'ADMIN_ACQ_FUND_TAG' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 398, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 399, 'ADMIN_AGE_PROTECT_RULE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 400, 'ADMIN_ASSET_COPY_TEMPLATE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 401, 'ADMIN_BOOKING_RESERVATION_ATTR_MAP' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 402, 'ADMIN_CIRC_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 403, 'ADMIN_CIRC_MOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 404, 'ADMIN_CLAIM_POLICY' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 405, 'ADMIN_CONFIG_REMOTE_ACCOUNT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 406, 'ADMIN_FIELD_DOC' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 407, 'ADMIN_GLOBAL_FLAG' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 408, 'ADMIN_GROUP_PENALTY_THRESHOLD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 409, 'ADMIN_HOLD_CANCEL_CAUSE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 410, 'ADMIN_HOLD_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 411, 'ADMIN_IDENT_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 412, 'ADMIN_IMPORT_ITEM_ATTR_DEF' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 413, 'ADMIN_INDEX_NORMALIZER' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 414, 'ADMIN_INVOICE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 415, 'ADMIN_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 416, 'ADMIN_INVOICE_PAYMENT_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 417, 'ADMIN_LINEITEM_MARC_ATTR_DEF' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 418, 'ADMIN_MARC_CODE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 419, 'ADMIN_MAX_FINE_RULE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 420, 'ADMIN_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 421, 'ADMIN_ORG_UNIT_SETTING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 422, 'ADMIN_RECURRING_FINE_RULE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 423, 'ADMIN_SERIAL_SUBSCRIPTION' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 424, 'ADMIN_STANDING_PENALTY' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 425, 'ADMIN_SURVEY' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 426, 'ADMIN_USER_REQUEST_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 427, 'ADMIN_USER_SETTING_GROUP' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 428, 'ADMIN_USER_SETTING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 429, 'ADMIN_Z3950_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 430, 'CREATE_BIB_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 431, 'CREATE_BIBLIO_FINGERPRINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 432, 'CREATE_BIB_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 433, 'CREATE_BILLING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 434, 'CREATE_CN_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 435, 'CREATE_COPY_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 436, 'CREATE_INVOICE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 437, 'CREATE_INVOICE_ITEM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 438, 'CREATE_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 439, 'CREATE_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 440, 'CREATE_METABIB_CLASS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 441, 'CREATE_METABIB_SEARCH_ALIAS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 442, 'CREATE_USER_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 443, 'DELETE_BIB_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 444, 'DELETE_BIBLIO_FINGERPRINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 445, 'DELETE_BIB_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 446, 'DELETE_BILLING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 447, 'DELETE_CN_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 448, 'DELETE_COPY_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 449, 'DELETE_INVOICE_ITEM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 450, 'DELETE_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 451, 'DELETE_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 452, 'DELETE_METABIB_CLASS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 453, 'DELETE_METABIB_SEARCH_ALIAS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 454, 'DELETE_USER_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 455, 'MANAGE_CLAIM' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 456, 'UPDATE_BIB_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 457, 'UPDATE_BIBLIO_FINGERPRINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 458, 'UPDATE_BIB_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 459, 'UPDATE_BILLING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 460, 'UPDATE_CN_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 461, 'UPDATE_COPY_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 462, 'UPDATE_INVOICE_ITEM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 463, 'UPDATE_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 464, 'UPDATE_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 465, 'UPDATE_METABIB_CLASS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 466, 'UPDATE_METABIB_SEARCH_ALIAS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 467, 'UPDATE_USER_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 468, 'user_request.create' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 469, 'user_request.delete' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 470, 'user_request.update' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 471, 'user_request.view' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 472, 'VIEW_ACQ_FUND_ALLOCATION_PERCENT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 473, 'VIEW_CIRC_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 474, 'VIEW_CLAIM' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 475, 'VIEW_GROUP_PENALTY_THRESHOLD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 476, 'VIEW_HOLD_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 477, 'VIEW_INVOICE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 478, 'VIEW_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 479, 'VIEW_SERIAL_SUBSCRIPTION' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 480, 'VIEW_STANDING_PENALTY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 396, 'ADMIN_ACQ_CLAIM' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 397, 'ADMIN_ACQ_CLAIM_EVENT_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 398, 'ADMIN_ACQ_CLAIM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 399, 'ADMIN_ACQ_DISTRIB_FORMULA' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 400, 'ADMIN_ACQ_FISCAL_YEAR' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 401, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 402, 'ADMIN_ACQ_FUND_TAG' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 403, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 404, 'ADMIN_AGE_PROTECT_RULE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 405, 'ADMIN_ASSET_COPY_TEMPLATE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 406, 'ADMIN_BOOKING_RESERVATION_ATTR_MAP' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 407, 'ADMIN_CIRC_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 408, 'ADMIN_CIRC_MOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 409, 'ADMIN_CLAIM_POLICY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 410, 'ADMIN_CONFIG_REMOTE_ACCOUNT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 411, 'ADMIN_FIELD_DOC' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 412, 'ADMIN_GLOBAL_FLAG' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 413, 'ADMIN_GROUP_PENALTY_THRESHOLD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 414, 'ADMIN_HOLD_CANCEL_CAUSE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 415, 'ADMIN_HOLD_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 416, 'ADMIN_IDENT_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 417, 'ADMIN_IMPORT_ITEM_ATTR_DEF' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 418, 'ADMIN_INDEX_NORMALIZER' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 419, 'ADMIN_INVOICE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 420, 'ADMIN_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 421, 'ADMIN_INVOICE_PAYMENT_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 422, 'ADMIN_LINEITEM_MARC_ATTR_DEF' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 423, 'ADMIN_MARC_CODE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 424, 'ADMIN_MAX_FINE_RULE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 425, 'ADMIN_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 426, 'ADMIN_ORG_UNIT_SETTING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 427, 'ADMIN_RECURRING_FINE_RULE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 428, 'ADMIN_STANDING_PENALTY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 429, 'ADMIN_SURVEY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 430, 'ADMIN_USER_REQUEST_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 431, 'ADMIN_USER_SETTING_GROUP' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 432, 'ADMIN_USER_SETTING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 433, 'ADMIN_Z3950_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 434, 'CREATE_BIB_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 435, 'CREATE_BIBLIO_FINGERPRINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 436, 'CREATE_BIB_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 437, 'CREATE_BILLING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 438, 'CREATE_CN_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 439, 'CREATE_COPY_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 440, 'CREATE_INVOICE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 441, 'CREATE_INVOICE_ITEM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 442, 'CREATE_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 443, 'CREATE_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 444, 'CREATE_METABIB_CLASS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 445, 'CREATE_METABIB_SEARCH_ALIAS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 446, 'CREATE_USER_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 447, 'DELETE_BIB_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 448, 'DELETE_BIBLIO_FINGERPRINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 449, 'DELETE_BIB_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 450, 'DELETE_BILLING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 451, 'DELETE_CN_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 452, 'DELETE_COPY_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 453, 'DELETE_INVOICE_ITEM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 454, 'DELETE_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 455, 'DELETE_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 456, 'DELETE_METABIB_CLASS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 457, 'DELETE_METABIB_SEARCH_ALIAS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 458, 'DELETE_USER_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 459, 'MANAGE_CLAIM' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 460, 'UPDATE_BIB_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 461, 'UPDATE_BIBLIO_FINGERPRINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 462, 'UPDATE_BIB_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 463, 'UPDATE_BILLING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 464, 'UPDATE_CN_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 465, 'UPDATE_COPY_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 466, 'UPDATE_INVOICE_ITEM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 467, 'UPDATE_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 468, 'UPDATE_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 469, 'UPDATE_METABIB_CLASS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 470, 'UPDATE_METABIB_SEARCH_ALIAS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 471, 'UPDATE_USER_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 472, 'user_request.create' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 473, 'user_request.delete' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 474, 'user_request.update' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 475, 'user_request.view' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 476, 'VIEW_ACQ_FUND_ALLOCATION_PERCENT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 477, 'VIEW_CIRC_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 478, 'VIEW_CLAIM' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 479, 'VIEW_GROUP_PENALTY_THRESHOLD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 480, 'VIEW_HOLD_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 481, 'VIEW_INVOICE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 482, 'VIEW_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 483, 'VIEW_SERIAL_SUBSCRIPTION' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 484, 'VIEW_STANDING_PENALTY' );
 
 -- For every permission in the temp_perm table that has a matching
 -- permission in the real table: record the original id.
@@ -3234,8 +3263,19 @@
 INSERT INTO action_trigger.event_definition
 (id, active, owner, name, hook, validator, reactor, cleanup_success, cleanup_failure, delay, delay_field, group_field, template) VALUES
 (23, true, 1, 'PO JEDI', 'acqpo.activated', 'Acq::PurchaseOrderEDIRequired', 'GeneratePurchaseOrderJEDI', NULL, NULL, '00:05:00', NULL, NULL,
-$$[%- USE date -%]
-[%# start JEDI document -%]
+$$
+[%- USE date -%]
+[%# start JEDI document 
+  # Vendor specific kludges:
+  # BT      - vendcode goes to NAD/BY *suffix*  w/ 91 qualifier
+  # INGRAM  - vendcode goes to NAD/BY *segment* w/ 91 qualifier (separately)
+  # BRODART - vendcode goes to FTX segment (lineitem level)
+-%]
+[%- 
+IF target.provider.edi_default.vendcode && target.provider.code == 'BRODART';
+    xtra_ftx = target.provider.edi_default.vendcode;
+END;
+-%]
 [%- BLOCK big_block -%]
 {
    "recipient":"[% target.provider.san %]",
@@ -3244,23 +3284,27 @@
      "ORDERS":[ "order", {
         "po_number":[% target.id %],
         "date":"[% date.format(date.now, '%Y%m%d') %]",
-        "buyer":[{
-            [%- IF target.provider.edi_default.vendcode -%]
-                "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]", 
-                "id-qualifier": 91
+        "buyer":[
+            [%   IF   target.provider.edi_default.vendcode && (target.provider.code == 'BT' || target.provider.name.match('(?i)^BAKER & TAYLOR'))  -%]
+                {"id-qualifier": 91, "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]"}
+            [%- ELSIF target.provider.edi_default.vendcode && target.provider.code == 'INGRAM' -%]
+                {"id":"[% target.ordering_agency.mailing_address.san %]"},
+                {"id-qualifier": 91, "id":"[% target.provider.edi_default.vendcode %]"}
             [%- ELSE -%]
-                "id":"[% target.ordering_agency.mailing_address.san %]"
-            [%- END  -%]
-        }],
-        "vendor":[ 
+                {"id":"[% target.ordering_agency.mailing_address.san %]"}
+            [%- END -%]
+        ],
+        "vendor":[
             [%- # target.provider.name (target.provider.id) -%]
             "[% target.provider.san %]",
             {"id-qualifier": 92, "id":"[% target.provider.id %]"}
         ],
         "currency":"[% target.provider.currency_type %]",
+                
         "items":[
-        [% FOR li IN target.lineitems %]
+        [%- FOR li IN target.lineitems %]
         {
+            "line_index":"[% li.id %]",
             "identifiers":[   [%-# li.isbns = helpers.get_li_isbns(li.attributes) %]
             [% FOR isbn IN helpers.get_li_isbns(li.attributes) -%]
                 [% IF isbn.length == 13 -%]
@@ -3269,29 +3313,41 @@
                 {"id-qualifier":"IB","id":"[% isbn %]"},
                 [%- END %]
             [% END %]
-                {"id-qualifier":"SA","id":"[% li.id %]"}
+                {"id-qualifier":"IN","id":"[% li.id %]"}
             ],
             "price":[% li.estimated_unit_price || '0.00' %],
             "desc":[
-                {"BTI":"[% helpers.get_li_attr('title',     '', li.attributes) %]"}, 
+                {"BTI":"[% helpers.get_li_attr('title',     '', li.attributes) %]"},
                 {"BPU":"[% helpers.get_li_attr('publisher', '', li.attributes) %]"},
                 {"BPD":"[% helpers.get_li_attr('pubdate',   '', li.attributes) %]"},
                 {"BPH":"[% helpers.get_li_attr('pagination','', li.attributes) %]"}
             ],
+            [%- ftx_vals = []; 
+                FOR note IN li.lineitem_notes; 
+                    NEXT UNLESS note.vendor_public == 't'; 
+                    ftx_vals.push(note.value); 
+                END; 
+                IF xtra_ftx;           ftx_vals.unshift(xtra_ftx); END; 
+                IF ftx_vals.size == 0; ftx_vals.unshift('');       END;  # BT needs FTX+LIN for every LI, even if it is an empty one
+            -%]
+
+            "free-text":[ 
+                [% FOR note IN ftx_vals -%] "[% note %]"[% UNLESS loop.last %], [% END %][% END %] 
+            ],            
             "quantity":[% li.lineitem_details.size %]
         }[% UNLESS loop.last %],[% END %]
         [%-# TODO: lineitem details (later) -%]
         [% END %]
         ],
         "line_items":[% target.lineitems.size %]
-     }]  [% # close ORDERS array %]
-   }]    [% # close  body  array %]
+     }]  [%# close ORDERS array %]
+   }]    [%# close  body  array %]
 }
 [% END %]
 [% tempo = PROCESS big_block; helpers.escape_json(tempo) %]
-$$
-);
 
+$$);
+
 INSERT INTO action_trigger.environment (event_def, path) VALUES 
   (23, 'lineitems.attributes'), 
   (23, 'lineitems.lineitem_details'), 
@@ -6049,10 +6105,10 @@
 CREATE OR REPLACE FUNCTION asset.acp_status_changed()
 RETURNS TRIGGER AS $$
 BEGIN
-	IF NEW.status <> OLD.status THEN
-		NEW.status_changed_time := now();
-	END IF;
-	RETURN NEW;
+    IF NEW.status <> OLD.status THEN
+        NEW.status_changed_time := now();
+    END IF;
+    RETURN NEW;
 END;
 $$ LANGUAGE plpgsql;
 
@@ -6263,6 +6319,23 @@
 
 ALTER TABLE action.circulation DROP CONSTRAINT action_circulation_target_copy_fkey;
 
+-- Rebuild dependent views
+
+DROP VIEW IF EXISTS action.billable_circulations;
+
+CREATE OR REPLACE VIEW action.billable_circulations AS
+    SELECT  *
+      FROM  action.circulation
+      WHERE xact_finish IS NULL;
+
+DROP VIEW IF EXISTS action.open_circulation;
+
+CREATE OR REPLACE VIEW action.open_circulation AS
+    SELECT  *
+      FROM  action.circulation
+      WHERE checkin_time IS NULL
+      ORDER BY due_date;
+
 CREATE OR REPLACE FUNCTION action.age_circ_on_delete () RETURNS TRIGGER AS $$
 DECLARE
 found char := 'N';
@@ -6308,8 +6381,123 @@
 
 -- Adding circ.holds.target_skip_me OU setting logic to the pre-matchpoint tests
 
-CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
 DECLARE
+    current_requestor_group    permission.grp_tree%ROWTYPE;
+    requestor_object    actor.usr%ROWTYPE;
+    user_object        actor.usr%ROWTYPE;
+    item_object        asset.copy%ROWTYPE;
+    item_cn_object        asset.call_number%ROWTYPE;
+    rec_descriptor        metabib.rec_descriptor%ROWTYPE;
+    current_mp_weight    FLOAT;
+    matchpoint_weight    FLOAT;
+    tmp_weight        FLOAT;
+    current_mp        config.hold_matrix_matchpoint%ROWTYPE;
+    matchpoint        config.hold_matrix_matchpoint%ROWTYPE;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+    SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
+    SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+    SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+    SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r WHERE r.record = item_cn_object.record;
+
+    PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
+
+    IF NOT FOUND THEN
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = requestor_object.profile;
+    ELSE
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = user_object.profile;
+    END IF;
+
+    LOOP 
+        -- for each potential matchpoint for this ou and group ...
+        FOR current_mp IN
+            SELECT    m.*
+              FROM    config.hold_matrix_matchpoint m
+              WHERE    m.requestor_grp = current_requestor_group.id AND m.active
+              ORDER BY    CASE WHEN m.circ_modifier    IS NOT NULL THEN 16 ELSE 0 END +
+                    CASE WHEN m.juvenile_flag    IS NOT NULL THEN 16 ELSE 0 END +
+                    CASE WHEN m.marc_type        IS NOT NULL THEN 8 ELSE 0 END +
+                    CASE WHEN m.marc_form        IS NOT NULL THEN 4 ELSE 0 END +
+                    CASE WHEN m.marc_vr_format    IS NOT NULL THEN 2 ELSE 0 END +
+                    CASE WHEN m.ref_flag        IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
+
+            current_mp_weight := 5.0;
+
+            IF current_mp.circ_modifier IS NOT NULL THEN
+                CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
+            END IF;
+
+            IF current_mp.marc_type IS NOT NULL THEN
+                IF item_object.circ_as_type IS NOT NULL THEN
+                    CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
+                ELSE
+                    CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
+                END IF;
+            END IF;
+
+            IF current_mp.marc_form IS NOT NULL THEN
+                CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
+            END IF;
+
+            IF current_mp.marc_vr_format IS NOT NULL THEN
+                CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
+            END IF;
+
+            IF current_mp.juvenile_flag IS NOT NULL THEN
+                CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
+            END IF;
+
+            IF current_mp.ref_flag IS NOT NULL THEN
+                CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
+            END IF;
+
+
+            -- caclulate the rule match weight
+            IF current_mp.item_owning_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.item_circ_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.pickup_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.request_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.user_home_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            -- set the matchpoint if we found the best one
+            IF matchpoint_weight IS NULL OR matchpoint_weight > current_mp_weight THEN
+                matchpoint = current_mp;
+                matchpoint_weight = current_mp_weight;
+            END IF;
+
+        END LOOP;
+
+        EXIT WHEN current_requestor_group.parent IS NULL OR matchpoint.id IS NOT NULL;
+
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = current_requestor_group.parent;
+    END LOOP;
+
+    RETURN matchpoint.id;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
+DECLARE
     matchpoint_id        INT;
     user_object        actor.usr%ROWTYPE;
     age_protect_object    config.rule_age_hold_protect%ROWTYPE;
@@ -6410,22 +6598,7 @@
         END IF;
     END IF;
  
-    FOR standing_penalty IN
-        SELECT  DISTINCT csp.*
-          FROM  actor.usr_standing_penalty usp
-                JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
-          WHERE usr = match_user
-                AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
-                AND (usp.stop_date IS NULL or usp.stop_date > NOW())
-                AND csp.block_list LIKE '%HOLD%' LOOP
-
-        result.fail_part := standing_penalty.name;
-        result.success := FALSE;
-        done := TRUE;
-        RETURN NEXT result;
-    END LOOP;
-
-    IF hold_test.stop_blocked_user IS TRUE THEN
+    IF NOT retargetting THEN
         FOR standing_penalty IN
             SELECT  DISTINCT csp.*
               FROM  actor.usr_standing_penalty usp
@@ -6433,29 +6606,46 @@
               WHERE usr = match_user
                     AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
                     AND (usp.stop_date IS NULL or usp.stop_date > NOW())
-                    AND csp.block_list LIKE '%CIRC%' LOOP
+                    AND csp.block_list LIKE '%HOLD%' LOOP
     
             result.fail_part := standing_penalty.name;
             result.success := FALSE;
             done := TRUE;
             RETURN NEXT result;
         END LOOP;
-    END IF;
-
-    IF hold_test.max_holds IS NOT NULL THEN
-        SELECT    INTO hold_count COUNT(*)
-          FROM    action.hold_request
-          WHERE    usr = match_user
-            AND fulfillment_time IS NULL
-            AND cancel_time IS NULL
-            AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
-
-        IF hold_count >= hold_test.max_holds THEN
-            result.fail_part := 'config.hold_matrix_test.max_holds';
-            result.success := FALSE;
-            done := TRUE;
-            RETURN NEXT result;
+    
+        IF hold_test.stop_blocked_user IS TRUE THEN
+            FOR standing_penalty IN
+                SELECT  DISTINCT csp.*
+                  FROM  actor.usr_standing_penalty usp
+                        JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+                  WHERE usr = match_user
+                        AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+                        AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+                        AND csp.block_list LIKE '%CIRC%' LOOP
+        
+                result.fail_part := standing_penalty.name;
+                result.success := FALSE;
+                done := TRUE;
+                RETURN NEXT result;
+            END LOOP;
         END IF;
+    
+        IF hold_test.max_holds IS NOT NULL THEN
+            SELECT    INTO hold_count COUNT(*)
+              FROM    action.hold_request
+              WHERE    usr = match_user
+                AND fulfillment_time IS NULL
+                AND cancel_time IS NULL
+                AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
+    
+            IF hold_count >= hold_test.max_holds THEN
+                result.fail_part := 'config.hold_matrix_test.max_holds';
+                result.success := FALSE;
+                done := TRUE;
+                RETURN NEXT result;
+            END IF;
+        END IF;
     END IF;
 
     IF item_object.age_protect IS NOT NULL THEN
@@ -6485,6 +6675,14 @@
 END;
 $func$ LANGUAGE plpgsql;
 
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+    SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, FALSE);
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION action.hold_retarget_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+    SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, TRUE );
+$func$ LANGUAGE SQL;
+
 -- New post-delete trigger to propagate deletions to parent(s)
 
 CREATE OR REPLACE FUNCTION action.age_parent_circ_on_delete () RETURNS TRIGGER AS $$
@@ -7107,7 +7305,7 @@
         END;
         $func$ LANGUAGE 'plpgsql';
     $$;
-    RETURN TRUE;
+	RETURN TRUE;
 END;
 $creator$ LANGUAGE 'plpgsql';
 
@@ -7147,7 +7345,7 @@
     PERFORM auditor.create_auditor_func(sch, tbl);
     PERFORM auditor.create_auditor_update_trigger(sch, tbl);
     PERFORM auditor.create_auditor_lifecycle(sch, tbl);
-	RETURN TRUE;
+    RETURN TRUE;
 END;
 $creator$ LANGUAGE 'plpgsql';
 
@@ -7175,7 +7373,7 @@
 ALTER TABLE AUDITOR.actor_usr_history ADD COLUMN 
 	claims_never_checked_out_count INT;
 
-DROP VIEW auditor.actor_usr_lifecycle;
+DROP VIEW IF EXISTS auditor.actor_usr_lifecycle;
 
 SELECT auditor.create_auditor_lifecycle( 'actor', 'usr' );
 
@@ -7941,7 +8139,7 @@
 	                               DEFERRABLE INITIALLY DEFERRED,
     max_fine       NUMERIC(8,2),
     elbow_room     INTERVAL,
-    CONSTRAINT brt_name_or_record_once_per_owner UNIQUE(owner, name, record)
+    CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record)
 );
 
 CREATE TABLE booking.resource (
@@ -8564,7 +8762,7 @@
 ALTER TABLE auditor.actor_org_unit_history
 	ADD COLUMN fiscal_calendar INT;
 
-DROP VIEW auditor.actor_org_unit_lifecycle;
+DROP VIEW IF EXISTS auditor.actor_org_unit_lifecycle;
 
 SELECT auditor.create_auditor_lifecycle( 'actor', 'org_unit' );
 
@@ -8969,6 +9167,133 @@
 END;
 $$ LANGUAGE plpgsql;
 
+COMMENT ON FUNCTION actor.usr_purge_data(INT, INT) IS $$
+/**
+ * Finds rows dependent on a given row in actor.usr and either deletes them
+ * or reassigns them to a different user.
+ */
+$$;
+
+CREATE OR REPLACE FUNCTION actor.usr_delete(
+	src_usr  IN INTEGER,
+	dest_usr IN INTEGER
+) RETURNS VOID AS $$
+DECLARE
+	old_profile actor.usr.profile%type;
+	old_home_ou actor.usr.home_ou%type;
+	new_profile actor.usr.profile%type;
+	new_home_ou actor.usr.home_ou%type;
+	new_name    text;
+	new_dob     actor.usr.dob%type;
+BEGIN
+	SELECT
+		id || '-PURGED-' || now(),
+		profile,
+		home_ou,
+		dob
+	INTO
+		new_name,
+		old_profile,
+		old_home_ou,
+		new_dob
+	FROM
+		actor.usr
+	WHERE
+		id = src_usr;
+	--
+	-- Quit if no such user
+	--
+	IF old_profile IS NULL THEN
+		RETURN;
+	END IF;
+	--
+	perform actor.usr_purge_data( src_usr, dest_usr );
+	--
+	-- Find the root grp_tree and the root org_unit.  This would be simpler if we 
+	-- could assume that there is only one root.  Theoretically, someday, maybe,
+	-- there could be multiple roots, so we take extra trouble to get the right ones.
+	--
+	SELECT
+		id
+	INTO
+		new_profile
+	FROM
+		permission.grp_ancestors( old_profile )
+	WHERE
+		parent is null;
+	--
+	SELECT
+		id
+	INTO
+		new_home_ou
+	FROM
+		actor.org_unit_ancestors( old_home_ou )
+	WHERE
+		parent_ou is null;
+	--
+	-- Truncate date of birth
+	--
+	IF new_dob IS NOT NULL THEN
+		new_dob := date_trunc( 'year', new_dob );
+	END IF;
+	--
+	UPDATE
+		actor.usr
+		SET
+			card = NULL,
+			profile = new_profile,
+			usrname = new_name,
+			email = NULL,
+			passwd = random()::text,
+			standing = DEFAULT,
+			ident_type = 
+			(
+				SELECT MIN( id )
+				FROM config.identification_type
+			),
+			ident_value = NULL,
+			ident_type2 = NULL,
+			ident_value2 = NULL,
+			net_access_level = DEFAULT,
+			photo_url = NULL,
+			prefix = NULL,
+			first_given_name = new_name,
+			second_given_name = NULL,
+			family_name = new_name,
+			suffix = NULL,
+			alias = NULL,
+			day_phone = NULL,
+			evening_phone = NULL,
+			other_phone = NULL,
+			mailing_address = NULL,
+			billing_address = NULL,
+			home_ou = new_home_ou,
+			dob = new_dob,
+			active = FALSE,
+			master_account = DEFAULT, 
+			super_user = DEFAULT,
+			barred = FALSE,
+			deleted = TRUE,
+			juvenile = DEFAULT,
+			usrgroup = 0,
+			claims_returned_count = DEFAULT,
+			credit_forward_balance = DEFAULT,
+			last_xact_id = DEFAULT,
+			alert_message = NULL,
+			create_date = now(),
+			expire_date = now()
+	WHERE
+		id = src_usr;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION actor.usr_delete(INT, INT) IS $$
+/**
+ * Logically deletes a user.  Removes personally identifiable information,
+ * and purges associated data in other tables.
+ */
+$$;
+
 -- INSERT INTO config.copy_status (id,name) VALUES (15,oils_i18n_gettext(15, 'On reservation shelf', 'ccs', 'name'));
 
 ALTER TABLE acq.fund
@@ -9053,7 +9378,11 @@
 $$;
 
 CREATE OR REPLACE VIEW money.billable_xact_summary_location_view AS
-	SELECT * FROM money.materialized_billable_xact_summary;
+    SELECT  m.*, COALESCE(c.circ_lib, g.billing_location, r.pickup_lib) AS billing_location
+      FROM  money.materialized_billable_xact_summary m
+            LEFT JOIN action.circulation c ON (c.id = m.id)
+            LEFT JOIN money.grocery g ON (g.id = m.id)
+            LEFT JOIN booking.reservation r ON (r.id = m.id);
 
 CREATE TABLE config.marc21_rec_type_map (
     code        TEXT    PRIMARY KEY,
@@ -10359,6 +10688,8 @@
 INSERT INTO config.internal_flag (name) VALUES ('ingest.disable_metabib_rec_descriptor');
 INSERT INTO config.internal_flag (name) VALUES ('ingest.disable_metabib_field_entry');
 INSERT INTO config.internal_flag (name) VALUES ('ingest.disable_authority_linking');
+INSERT INTO config.internal_flag (name) VALUES ('ingest.metarecord_mapping.skip_on_update');
+INSERT INTO config.internal_flag (name) VALUES ('ingest.assume_inserts_only');
 
 CREATE TABLE authority.bib_linking (
     id          BIGSERIAL   PRIMARY KEY,
@@ -10387,7 +10718,10 @@
 
 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_rec_descriptor( bib_id BIGINT ) RETURNS VOID AS $func$
 BEGIN
-    DELETE FROM metabib.rec_descriptor WHERE record = bib_id;
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        DELETE FROM metabib.rec_descriptor WHERE record = bib_id;
+    END IF;
     INSERT INTO metabib.rec_descriptor (record, item_type, item_form, bib_level, control_type, enc_level, audience, lit_form, type_mat, cat_form, pub_status, item_lang, vr_format, date1, date2)
         SELECT  bib_id,
                 biblio.marc21_extract_fixed_field( bib_id, 'Type' ),
@@ -10406,8 +10740,8 @@
                             JOIN config.marc21_physical_characteristic_subfield_map s ON (s.id = p.subfield)
                             JOIN config.marc21_physical_characteristic_value_map v ON (v.id = p.value)
                       WHERE p.ptype = 'v' AND s.subfield = 'e'    ),
-                biblio.marc21_extract_fixed_field( bib_id, 'Date1'),
-                biblio.marc21_extract_fixed_field( bib_id, 'Date2');
+                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date1'), ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
+                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date2'), ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
 
     RETURN;
 END;
@@ -10436,13 +10770,15 @@
     fclass          RECORD;
     ind_data        metabib.field_entry_template%ROWTYPE;
 BEGIN
-    FOR fclass IN SELECT * FROM config.metabib_class LOOP
-        -- RAISE NOTICE 'Emptying out %', fclass.name;
-        EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
-    END LOOP;
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        FOR fclass IN SELECT * FROM config.metabib_class LOOP
+            -- RAISE NOTICE 'Emptying out %', fclass.name;
+            EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
+        END LOOP;
+        DELETE FROM metabib.facet_entry WHERE source = bib_id;
+    END IF;
 
-    DELETE FROM metabib.facet_entry WHERE source = bib_id;
-
     FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
         IF ind_data.field < 0 THEN
             ind_data.field = -1 * ind_data.field;
@@ -10583,7 +10919,10 @@
 
 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_full_rec( bib_id BIGINT ) RETURNS VOID AS $func$
 BEGIN
-    DELETE FROM metabib.real_full_rec WHERE record = bib_id;
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        DELETE FROM metabib.real_full_rec WHERE record = bib_id;
+    END IF;
     INSERT INTO metabib.real_full_rec (record, tag, ind1, ind2, subfield, value)
         SELECT record, tag, ind1, ind2, subfield, value FROM biblio.flatten_marc( bib_id );
 
@@ -10648,7 +10987,10 @@
             PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
         END IF;
     ELSE -- we're doing an update, and we're not deleted, remap
-        PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
+        END IF;
     END IF;
 
     RETURN NEW;
@@ -10660,8 +11002,7 @@
 
 DROP TRIGGER IF EXISTS zzz_update_materialized_simple_rec_delete_tgr ON biblio.record_entry;
 
-CREATE OR REPLACE FUNCTION oils_xpath_table ( key TEXT, document_field TEXT, relation_name TEXT, xpaths TEXT, criteria TEXT )
-RETURNS SETOF RECORD AS $func$
+CREATE OR REPLACE FUNCTION oils_xpath_table ( key TEXT, document_field TEXT, relation_name TEXT, xpaths TEXT, criteria TEXT ) RETURNS SETOF RECORD AS $func$
 DECLARE
     xpath_list  TEXT[];
     select_list TEXT[];
@@ -10671,52 +11012,56 @@
     empty_test  RECORD;
 BEGIN
     xpath_list := STRING_TO_ARRAY( xpaths, '|' );
-
+ 
     select_list := ARRAY_APPEND( select_list, key || '::INT AS key' );
-
+ 
     FOR i IN 1 .. ARRAY_UPPER(xpath_list,1) LOOP
-        select_list := ARRAY_APPEND(
-            select_list,
-            $sel$
-            EXPLODE_ARRAY(
-                COALESCE(
-                    NULLIF(
-                        oils_xpath(
-                            $sel$ ||
-                                quote_literal(
-                                    CASE
-                                        WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
-                                        ELSE xpath_list[i] || '//text()'
-                                    END
-                                ) ||
-                            $sel$,
-                            $sel$ || document_field || $sel$
+        IF xpath_list[i] = 'null()' THEN
+            select_list := ARRAY_APPEND( select_list, 'NULL::TEXT AS c_' || i );
+        ELSE
+            select_list := ARRAY_APPEND(
+                select_list,
+                $sel$
+                EXPLODE_ARRAY(
+                    COALESCE(
+                        NULLIF(
+                            oils_xpath(
+                                $sel$ ||
+                                    quote_literal(
+                                        CASE
+                                            WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
+                                            ELSE xpath_list[i] || '//text()'
+                                        END
+                                    ) ||
+                                $sel$,
+                                $sel$ || document_field || $sel$
+                            ),
+                           '{}'::TEXT[]
                         ),
-                       '{}'::TEXT[]
-                    ),
-                    '{NULL}'::TEXT[]
-                )
-            ) AS c_$sel$ || i
-        );
-        where_list := ARRAY_APPEND(
-            where_list,
-            'c_' || i || ' IS NOT NULL'
-        );
+                        '{NULL}'::TEXT[]
+                    )
+                ) AS c_$sel$ || i
+            );
+            where_list := ARRAY_APPEND(
+                where_list,
+                'c_' || i || ' IS NOT NULL'
+            );
+        END IF;
     END LOOP;
-
+ 
     q := $q$
 SELECT * FROM (
     SELECT $q$ || ARRAY_TO_STRING( select_list, ', ' ) || $q$ FROM $q$ || relation_name || $q$ WHERE ($q$ || criteria || $q$)
 )x WHERE $q$ || ARRAY_TO_STRING( where_list, ' AND ' );
     -- RAISE NOTICE 'query: %', q;
-
+ 
     FOR out_record IN EXECUTE q LOOP
         RETURN NEXT out_record;
     END LOOP;
-
+ 
     RETURN;
 END;
-$func$ LANGUAGE PLPGSQL;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
 
 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
 DECLARE
@@ -11120,12 +11465,8 @@
 END;
 $creator$ LANGUAGE 'plpgsql';
 
-SELECT acq.create_acq_auditor ( 'acq', 'purchase_order' );
-CREATE INDEX acq_po_hist_id_idx            ON acq.acq_purchase_order_history( id );
+ALTER TABLE acq.lineitem DROP COLUMN item_count;
 
-SELECT acq.create_acq_auditor ( 'acq', 'lineitem' );
-CREATE INDEX acq_lineitem_hist_id_idx            ON acq.acq_lineitem_history( id );
-
 CREATE OR REPLACE VIEW acq.fund_debit_total AS
     SELECT  fund.id AS fund,
             fund_debit.encumbrance AS encumbrance,
@@ -12649,7 +12990,7 @@
 
 CREATE TABLE acq.lineitem_alert_text (
 	id               SERIAL         PRIMARY KEY,
-	code             TEXT           UNIQUE NOT NULL,
+	code             TEXT           NOT NULL,
 	description      TEXT,
 	owning_lib       INT            NOT NULL
 	                                REFERENCES actor.org_unit(id)
@@ -12914,13 +13255,7 @@
                 fund
         ) AS c USING ( fund );
 
-CREATE OR REPLACE FUNCTION actor.usr_merge(
-	src_usr INT,
-	dest_usr INT,
-	del_addrs BOOLEAN,
-	del_cards BOOLEAN,
-	deactivate_cards BOOLEAN
-) RETURNS VOID AS $$
+CREATE OR REPLACE FUNCTION actor.usr_merge( src_usr INT, dest_usr INT, del_addrs BOOLEAN, del_cards BOOLEAN, deactivate_cards BOOLEAN ) RETURNS VOID AS $$
 DECLARE
 	suffix TEXT;
 	bucket_row RECORD;
@@ -13098,7 +13433,7 @@
 
     -- acq.*
     UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
-    UPDATE acq.fund_transfer SET transfer_user = dest_usr WHERE transfer_user = src_usr;
+	UPDATE acq.fund_transfer SET transfer_user = dest_usr WHERE transfer_user = src_usr;
 
 	-- transfer picklists the same way we transfer buckets (see above)
 	FOR picklist_row in
@@ -13123,8 +13458,8 @@
     UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
     UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
     UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
-	UPDATE acq.provider_note SET creator = dest_usr WHERE creator = src_usr;
-	UPDATE acq.provider_note SET editor = dest_usr WHERE editor = src_usr;
+    UPDATE acq.provider_note SET creator = dest_usr WHERE creator = src_usr;
+    UPDATE acq.provider_note SET editor = dest_usr WHERE editor = src_usr;
     UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
     UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
     UPDATE acq.lineitem_usr_attr_definition SET usr = dest_usr WHERE usr = src_usr;
@@ -13272,6 +13607,10 @@
 TRUNCATE money.materialized_billable_xact_summary;
 INSERT INTO money.materialized_billable_xact_summary SELECT * FROM money.billable_xact_summary;
 
+-- Now redefine the view as a window onto the materialized view
+CREATE OR REPLACE VIEW money.billable_xact_summary AS
+    SELECT * FROM money.materialized_billable_xact_summary;
+
 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_nd(
     user_id    IN INTEGER,
     perm_code  IN TEXT
@@ -13486,41 +13825,34 @@
 $$ LANGUAGE 'plpgsql';
 
 ALTER TABLE acq.purchase_order
-	ADD COLUMN cancel_reason        INT REFERENCES acq.cancel_reason( id )
-	                                    DEFERRABLE INITIALLY DEFERRED;
-
-ALTER TABLE acq.acq_purchase_order_history
-	ADD COLUMN cancel_reason INTEGER;
-
-ALTER TABLE acq.purchase_order
+	ADD COLUMN cancel_reason INT
+		REFERENCES acq.cancel_reason( id )
+	    DEFERRABLE INITIALLY DEFERRED,
 	ADD COLUMN prepayment_required BOOLEAN NOT NULL DEFAULT FALSE;
 
-ALTER TABLE acq.acq_purchase_order_history
-	ADD COLUMN prepayment_required BOOLEAN NOT NULL DEFAULT FALSE;
+-- Build the history table and lifecycle view
+-- for acq.purchase_order
 
-ALTER TABLE acq.lineitem
-	ADD COLUMN cancel_reason        INT REFERENCES acq.cancel_reason( id )
-	                                    DEFERRABLE INITIALLY DEFERRED;
+SELECT acq.create_acq_auditor ( 'acq', 'purchase_order' );
 
-ALTER TABLE acq.acq_lineitem_history
-	ADD COLUMN cancel_reason INTEGER;
+CREATE INDEX acq_po_hist_id_idx            ON acq.acq_purchase_order_history( id );
 
 ALTER TABLE acq.lineitem
-	ADD COLUMN estimated_unit_price NUMERIC;
-
-ALTER TABLE acq.acq_lineitem_history
-	ADD COLUMN estimated_unit_price NUMERIC;
-
-ALTER TABLE acq.lineitem
+	ADD COLUMN cancel_reason INT
+		REFERENCES acq.cancel_reason( id )
+	    DEFERRABLE INITIALLY DEFERRED,
+	ADD COLUMN estimated_unit_price NUMERIC,
 	ADD COLUMN claim_policy INT
 		REFERENCES acq.claim_policy
-		DEFERRABLE INITIALLY DEFERRED;
+		DEFERRABLE INITIALLY DEFERRED,
+	ALTER COLUMN eg_bib_id SET DATA TYPE bigint;
 
-ALTER TABLE acq.acq_lineitem_history
-	ADD COLUMN claim_policy INT
-		REFERENCES acq.claim_policy
-		DEFERRABLE INITIALLY DEFERRED;
+-- Build the history table and lifecycle view
+-- for acq.lineitem
 
+SELECT acq.create_acq_auditor ( 'acq', 'lineitem' );
+CREATE INDEX acq_lineitem_hist_id_idx            ON acq.acq_lineitem_history( id );
+
 ALTER TABLE acq.lineitem_detail
 	ADD COLUMN cancel_reason        INT REFERENCES acq.cancel_reason( id )
 	                                    DEFERRABLE INITIALLY DEFERRED;
@@ -13818,10 +14150,10 @@
         END IF;
     END IF;
 
-    add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),''),'');
-    strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),''),'');
-    replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),''),'');
-    preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),''),'');
+    add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),','),'');
+    strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),','),'');
+    replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),','),'');
+    preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),','),'');
 
     output.add_rule := BTRIM(add_rule,',');
     output.replace_rule := BTRIM(replace_rule,',');
@@ -14398,7 +14730,8 @@
 UPDATE config.metabib_field SET label = name;
 ALTER TABLE config.metabib_field ALTER COLUMN label SET NOT NULL;
 
-ALTER TABLE config.metabib_field ADD CONSTRAINT field_class_fkey FOREIGN KEY (field_class) REFERENCES config.metabib_class (name);
+ALTER TABLE config.metabib_field ADD CONSTRAINT metabib_field_field_class_fkey
+	 FOREIGN KEY (field_class) REFERENCES config.metabib_class (name);
 
 ALTER TABLE config.metabib_field DROP CONSTRAINT metabib_field_field_class_check;
 
@@ -14753,7 +15086,12 @@
 END;
 $func$ LANGUAGE PLPGSQL;
 
-ALTER TABLE biblio.record_entry ADD COLUMN owner INT REFERENCES actor.org_unit (id);
+ALTER TABLE biblio.record_entry ADD COLUMN owner INT;
+ALTER TABLE biblio.record_entry
+	 ADD CONSTRAINT biblio_record_entry_owner_fkey FOREIGN KEY (owner)
+	 REFERENCES actor.org_unit (id)
+	 DEFERRABLE INITIALLY DEFERRED;
+
 ALTER TABLE biblio.record_entry ADD COLUMN share_depth INT;
 
 ALTER TABLE auditor.biblio_record_entry_history ADD COLUMN owner INT;
@@ -15239,23 +15577,23 @@
             SELECT * INTO circ_chain_tail FROM action.circ_chain(circ_chain_head.id) ORDER BY xact_start DESC LIMIT 1;
             EXIT WHEN circ_chain_tail.xact_finish IS NULL;
 
-            -- Now get the user setings, if any, to block purging if the user wants to keep more circs
+            -- Now get the user settings, if any, to block purging if the user wants to keep more circs
             usr_keep_age.value := NULL;
             SELECT * INTO usr_keep_age FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_age';
 
             usr_keep_start.value := NULL;
-            SELECT * INTO usr_keep_start FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_start_date';
+            SELECT * INTO usr_keep_start FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_start';
 
             IF usr_keep_age.value IS NOT NULL AND usr_keep_start.value IS NOT NULL THEN
-                IF oils_json_to_string(usr_keep_age.value)::INTERVAL > AGE(NOW(), oils_json_to_string(usr_keep_start.value)::TIMESTAMPTZ) THEN
-                    keep_age := AGE(NOW(), oils_json_to_string(usr_keep_start.value)::TIMESTAMPTZ);
+                IF oils_json_to_text(usr_keep_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ) THEN
+                    keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
                 ELSE
-                    keep_age := oils_json_to_string(usr_keep_age.value)::INTERVAL;
+                    keep_age := oils_json_to_text(usr_keep_age.value)::INTERVAL;
                 END IF;
             ELSIF usr_keep_start.value IS NOT NULL THEN
-                keep_age := AGE(NOW(), oils_json_to_string(usr_keep_start.value)::TIMESTAMPTZ);
+                keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
             ELSE
-                keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTEVAL );
+                keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTERVAL );
             END IF;
 
             EXIT WHEN AGE(NOW(), circ_chain_tail.xact_finish) < keep_age;
@@ -15405,6 +15743,8 @@
 	expected_date_offset   INTERVAL
 	-- acquisitions/business-side tables link to here
 );
+CREATE INDEX serial_subscription_record_idx ON serial.subscription (record_entry);
+CREATE INDEX serial_subscription_owner_idx ON serial.subscription (owning_lib);
 
 --at least one distribution per org_unit holding issues
 CREATE TABLE serial.distribution (
@@ -15436,6 +15776,8 @@
 	unit_label_prefix     TEXT,
 	unit_label_suffix     TEXT
 );
+CREATE INDEX serial_distribution_sub_idx ON serial.distribution (subscription);
+CREATE INDEX serial_distribution_holding_lib_idx ON serial.distribution (holding_lib);
 
 CREATE UNIQUE INDEX one_dist_per_sre_idx ON serial.distribution (record_entry);
 
@@ -15447,6 +15789,7 @@
 	                        DEFERRABLE INITIALLY DEFERRED,
 	routing_label   TEXT
 );
+CREATE INDEX serial_stream_dist_idx ON serial.stream (distribution);
 
 CREATE UNIQUE INDEX label_once_per_dist
 	ON serial.stream (distribution, routing_label)
@@ -15472,6 +15815,8 @@
 		(reader IS NULL AND department IS NOT NULL)
 	)
 );
+CREATE INDEX serial_routing_list_user_stream_idx ON serial.routing_list_user (stream);
+CREATE INDEX serial_routing_list_user_reader_idx ON serial.routing_list_user (reader);
 
 CREATE TABLE serial.caption_and_pattern (
 	id           SERIAL       PRIMARY KEY,
@@ -15498,6 +15843,7 @@
 	chron_4      TEXT,
 	chron_5      TEXT
 );
+CREATE INDEX serial_caption_and_pattern_sub_idx ON serial.caption_and_pattern (subscription);
 
 CREATE TABLE serial.issuance (
 	id              SERIAL    PRIMARY KEY,
@@ -15526,12 +15872,20 @@
 	holding_link_id INT
 	-- TODO: add columns for separate enumeration/chronology values
 );
+CREATE INDEX serial_issuance_sub_idx ON serial.issuance (subscription);
+CREATE INDEX serial_issuance_caption_and_pattern_idx ON serial.issuance (caption_and_pattern);
+CREATE INDEX serial_issuance_date_published_idx ON serial.issuance (date_published);
 
 CREATE TABLE serial.unit (
 	label           TEXT,
 	label_sort_key  TEXT,
 	contents        TEXT    NOT NULL
 ) INHERITS (asset.copy);
+CREATE UNIQUE INDEX unit_barcode_key ON serial.unit (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
+CREATE INDEX unit_cn_idx ON serial.unit (call_number);
+CREATE INDEX unit_avail_cn_idx ON serial.unit (call_number);
+CREATE INDEX unit_creator_idx  ON serial.unit ( creator );
+CREATE INDEX unit_editor_idx   ON serial.unit ( editor );
 
 ALTER TABLE serial.unit ADD PRIMARY KEY (id);
 
@@ -15573,6 +15927,12 @@
                             DEFAULT 'Expected',
 	shadowed        BOOL    NOT NULL DEFAULT FALSE
 );
+CREATE INDEX serial_item_stream_idx ON serial.item (stream);
+CREATE INDEX serial_item_issuance_idx ON serial.item (issuance);
+CREATE INDEX serial_item_unit_idx ON serial.item (unit);
+CREATE INDEX serial_item_uri_idx ON serial.item (uri);
+CREATE INDEX serial_item_date_received_idx ON serial.item (date_received);
+CREATE INDEX serial_item_status_idx ON serial.item (status);
 
 CREATE TABLE serial.item_note (
 	id          SERIAL  PRIMARY KEY,
@@ -15588,6 +15948,7 @@
 	title       TEXT    NOT NULL,
 	value       TEXT    NOT NULL
 );
+CREATE INDEX serial_item_note_item_idx ON serial.item_note (item);
 
 CREATE TABLE serial.basic_summary (
 	id                  SERIAL  PRIMARY KEY,
@@ -15599,6 +15960,7 @@
 	textual_holdings    TEXT,
 	show_generated      BOOL    NOT NULL DEFAULT TRUE
 );
+CREATE INDEX serial_basic_summary_dist_idx ON serial.basic_summary (distribution);
 
 CREATE TABLE serial.supplement_summary (
 	id                  SERIAL  PRIMARY KEY,
@@ -15610,6 +15972,7 @@
 	textual_holdings    TEXT,
 	show_generated      BOOL    NOT NULL DEFAULT TRUE
 );
+CREATE INDEX serial_supplement_summary_dist_idx ON serial.supplement_summary (distribution);
 
 CREATE TABLE serial.index_summary (
 	id                  SERIAL  PRIMARY KEY,
@@ -15621,6 +15984,7 @@
 	textual_holdings    TEXT,
 	show_generated      BOOL    NOT NULL DEFAULT TRUE
 );
+CREATE INDEX serial_index_summary_dist_idx ON serial.index_summary (distribution);
 
 -- DELETE FROM action_trigger.environment WHERE event_def IN (29,30); DELETE FROM action_trigger.event where event_def IN (29,30); DELETE FROM action_trigger.event_definition WHERE id IN (29,30); DELETE FROM action_trigger.hook WHERE key IN ('money.format.payment_receipt.email','money.format.payment_receipt.print'); DELETE FROM config.upgrade_log WHERE version = '0289'; -- from testing, this sql will remove these events, etc.
 
@@ -16070,6 +16434,64 @@
     SELECT authority.propagate_changes( authority, bib ) FROM authority.bib_linking WHERE authority = $1;
 $func$ LANGUAGE SQL;
 
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( TEXT ) RETURNS SETOF authority.full_rec AS $func$
+
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+
+my $xml = shift;
+my $r = MARC::Record->new_from_xml( $xml );
+
+return_next( { tag => 'LDR', value => $r->leader } );
+
+for my $f ( $r->fields ) {
+    if ($f->is_control_field) {
+        return_next({ tag => $f->tag, value => $f->data });
+    } else {
+        for my $s ($f->subfields) {
+            return_next({
+                tag      => $f->tag,
+                ind1     => $f->indicator(1),
+                ind2     => $f->indicator(2),
+                subfield => $s->[0],
+                value    => $s->[1]
+            });
+
+        }
+    }
+}
+
+return undef;
+
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
+DECLARE
+    auth    authority.record_entry%ROWTYPE;
+    output    authority.full_rec%ROWTYPE;
+    field    RECORD;
+BEGIN
+    SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
+
+    FOR field IN SELECT * FROM authority.flatten_marc( auth.marc ) LOOP
+        output.record := rid;
+        output.ind1 := field.ind1;
+        output.ind2 := field.ind2;
+        output.tag := field.tag;
+        output.subfield := field.subfield;
+        IF field.subfield IS NOT NULL THEN
+            output.value := naco_normalize(field.value, field.subfield);
+        ELSE
+            output.value := field.value;
+        END IF;
+
+        CONTINUE WHEN output.value IS NULL;
+
+        RETURN NEXT output;
+    END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
 -- authority.rec_descriptor appears to be unused currently
 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
 BEGIN
@@ -16152,8 +16574,6 @@
             NEW.marc,
             E'(</(?:[^:]*?:)?record>)',
             E'<datafield tag="901" ind1=" " ind2=" ">' ||
-                '<subfield code="a">' || NEW.arn_value || E'</subfield>' ||
-                '<subfield code="b">' || NEW.arn_source || E'</subfield>' ||
                 '<subfield code="c">' || NEW.id || E'</subfield>' ||
                 '<subfield code="t">' || TG_TABLE_SCHEMA || E'</subfield>' ||
              E'</datafield>\\1'
@@ -16255,6 +16675,18 @@
         TRUE
     );
 
+INSERT INTO config.global_flag (name, label, enabled)
+    VALUES (
+        'circ.holds.usr_not_requestor',
+        oils_i18n_gettext(
+            'circ.holds.usr_not_requestor',
+            'Holds: When testing hold matrix matchpoints, use the profile group of the receiving user instead of that of the requestor (affects staff-placed holds)',
+            'cgf',
+            'label'
+        ),
+        TRUE
+    );
+
 CREATE OR REPLACE FUNCTION maintain_control_numbers() RETURNS TRIGGER AS $func$
 use strict;
 use MARC::Record;
@@ -16721,6 +17153,7 @@
 	title        TEXT   NOT NULL,
 	value        TEXT   NOT NULL
 );
+CREATE INDEX serial_distribution_note_dist_idx ON serial.distribution_note (distribution);
 
 ------- Begin surgery on serial.unit
 
@@ -17341,7 +17774,7 @@
 	ADD COLUMN label_sortkey TEXT;
 
 CREATE INDEX asset_call_number_label_sortkey
-	ON asset.call_number(label_sortkey);
+	ON asset.call_number(oils_text_as_bytea(label_sortkey));
 
 ALTER TABLE auditor.asset_call_number_history
 	ADD COLUMN label_class BIGINT;
@@ -17970,13 +18403,6 @@
 ALTER TABLE authority.record_entry DROP COLUMN arn_value;
 ALTER TABLE authority.record_entry DROP COLUMN arn_source;
 
-DROP INDEX IF EXISTS authority.unique_by_heading_and_thesaurus;
-
-CREATE INDEX by_heading_and_thesaurus
-    ON authority.record_entry (authority.normalize_heading(marc))
-    WHERE deleted IS FALSE or deleted = FALSE
-;
-
 ALTER TABLE acq.provider_contact
 	ALTER COLUMN name SET NOT NULL;
 
@@ -18026,6 +18452,198 @@
 CREATE INDEX aud_bib_rec_entry_hist_editor_idx
 	ON auditor.biblio_record_entry_history ( editor );
 
+CREATE TABLE action.hold_request_note (
+
+    id     BIGSERIAL PRIMARY KEY,
+    hold   BIGINT    NOT NULL REFERENCES action.hold_request (id)
+                              ON DELETE CASCADE
+                              DEFERRABLE INITIALLY DEFERRED,
+    title  TEXT      NOT NULL,
+    body   TEXT      NOT NULL,
+    slip   BOOL      NOT NULL DEFAULT FALSE,
+    pub    BOOL      NOT NULL DEFAULT FALSE,
+    staff  BOOL      NOT NULL DEFAULT FALSE  -- created by staff
+
+);
+CREATE INDEX ahrn_hold_idx ON action.hold_request_note (hold);
+
+-- Tweak a constraint to add a CASCADE
+
+ALTER TABLE action.hold_notification DROP CONSTRAINT hold_notification_hold_fkey;
+
+ALTER TABLE action.hold_notification
+	ADD CONSTRAINT hold_notification_hold_fkey
+		FOREIGN KEY (hold) REFERENCES action.hold_request (id)
+		ON DELETE CASCADE
+		DEFERRABLE INITIALLY DEFERRED;
+
+CREATE TRIGGER asset_label_sortkey_trigger
+    BEFORE UPDATE OR INSERT ON asset.call_number
+    FOR EACH ROW EXECUTE PROCEDURE asset.label_normalizer();
+
+CREATE OR REPLACE FUNCTION container.clear_all_expired_circ_history_items( )
+RETURNS VOID AS $$
+--
+-- Delete expired circulation bucket items for all users that have
+-- a setting for patron.max_reading_list_interval.
+--
+DECLARE
+    today        TIMESTAMP WITH TIME ZONE;
+    threshold    TIMESTAMP WITH TIME ZONE;
+	usr_setting  RECORD;
+BEGIN
+	SELECT date_trunc( 'day', now() ) INTO today;
+	--
+	FOR usr_setting in
+		SELECT
+			usr,
+			value
+		FROM
+			actor.usr_setting
+		WHERE
+			name = 'patron.max_reading_list_interval'
+	LOOP
+		--
+		-- Make sure the setting is a valid interval
+		--
+		BEGIN
+			threshold := today - CAST( translate( usr_setting.value, '"', '' ) AS INTERVAL );
+		EXCEPTION
+			WHEN OTHERS THEN
+				RAISE NOTICE 'Invalid setting patron.max_reading_list_interval for user %: ''%''',
+					usr_setting.usr, usr_setting.value;
+				CONTINUE;
+		END;
+		--
+		--RAISE NOTICE 'User % threshold %', usr_setting.usr, threshold;
+		--
+    	DELETE FROM container.copy_bucket_item
+    	WHERE
+        	bucket IN
+        	(
+        	    SELECT
+        	        id
+        	    FROM
+        	        container.copy_bucket
+        	    WHERE
+        	        owner = usr_setting.usr
+        	        AND btype = 'circ_history'
+        	)
+        	AND create_time < threshold;
+	END LOOP;
+	--
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION container.clear_all_expired_circ_history_items( ) IS $$
+/*
+ * Delete expired circulation bucket items for all users that have
+ * a setting for patron.max_reading_list_interval.
+*/
+$$;
+
+CREATE OR REPLACE FUNCTION container.clear_expired_circ_history_items( 
+	 ac_usr IN INTEGER
+) RETURNS VOID AS $$
+--
+-- Delete old circulation bucket items for a specified user.
+-- "Old" means older than the interval specified by a
+-- user-level setting, if it is so specified.
+--
+DECLARE
+    threshold TIMESTAMP WITH TIME ZONE;
+BEGIN
+	-- Sanity check
+	IF ac_usr IS NULL THEN
+		RETURN;
+	END IF;
+	-- Determine the threshold date that defines "old".  Subtract the
+	-- interval from the system date, then truncate to midnight.
+	SELECT
+		date_trunc( 
+			'day',
+			now() - CAST( translate( value, '"', '' ) AS INTERVAL )
+		)
+	INTO
+		threshold
+	FROM
+		actor.usr_setting
+	WHERE
+		usr = ac_usr
+		AND name = 'patron.max_reading_list_interval';
+	--
+	IF threshold is null THEN
+		-- No interval defined; don't delete anything
+		-- RAISE NOTICE 'No interval defined for user %', ac_usr;
+		return;
+	END IF;
+	--
+	-- RAISE NOTICE 'Date threshold: %', threshold;
+	--
+	-- Threshold found; do the delete
+	delete from container.copy_bucket_item
+	where
+		bucket in
+		(
+			select
+				id
+			from
+				container.copy_bucket
+			where
+				owner = ac_usr
+				and btype = 'circ_history'
+		)
+		and create_time < threshold;
+	--
+	RETURN;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION container.clear_expired_circ_history_items( INTEGER ) IS $$
+/*
+ * Delete old circulation bucket items for a specified user.
+ * "Old" means older than the interval specified by a
+ * user-level setting, if it is so specified.
+*/
+$$;
+
+CREATE OR REPLACE VIEW reporter.hold_request_record AS
+SELECT  id,
+    target,
+    hold_type,
+    CASE
+        WHEN hold_type = 'T'
+            THEN target
+        WHEN hold_type = 'I'
+            THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = ahr.target)
+        WHEN hold_type = 'V'
+            THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
+        WHEN hold_type IN ('C','R','F')
+            THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
+        WHEN hold_type = 'M'
+            THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)
+    END AS bib_record
+  FROM  action.hold_request ahr;
+
+UPDATE  metabib.rec_descriptor
+  SET   date1=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date1, ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
+        date2=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date2, ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
+
+-- Change some ints to bigints:
+
+ALTER TABLE container.biblio_record_entry_bucket_item
+	ALTER COLUMN target_biblio_record_entry SET DATA TYPE bigint;
+
+ALTER TABLE vandelay.queued_bib_record
+	ALTER COLUMN imported_as SET DATA TYPE bigint;
+
+ALTER TABLE action.hold_copy_map
+	ALTER COLUMN id SET DATA TYPE bigint;
+
+-- Make due times get pushed to 23:59:59 on insert OR update
+DROP TRIGGER IF EXISTS push_due_date_tgr ON action.circulation;
+CREATE TRIGGER push_due_date_tgr BEFORE INSERT OR UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
+
 COMMIT;
 
 -- Some operations go outside of the transaction, because they may
@@ -18046,39 +18664,51 @@
 \qecho If any of these CREATE INDEX statements fails because the index already
 \qecho exists, ignore the failure.
 
-CREATE INDEX serial_subscription_record_idx ON serial.subscription (record_entry);
-CREATE INDEX serial_subscription_owner_idx ON serial.subscription (owning_lib);
-CREATE INDEX serial_caption_and_pattern_sub_idx ON serial.caption_and_pattern (subscription);
-CREATE INDEX serial_distribution_sub_idx ON serial.distribution (subscription);
-CREATE INDEX serial_distribution_holding_lib_idx ON serial.distribution (holding_lib);
-CREATE INDEX serial_distribution_note_dist_idx ON serial.distribution_note (distribution);
-CREATE INDEX serial_stream_dist_idx ON serial.stream (distribution);
-CREATE INDEX serial_routing_list_user_stream_idx ON serial.routing_list_user (stream);
-CREATE INDEX serial_routing_list_user_reader_idx ON serial.routing_list_user (reader);
-CREATE INDEX serial_issuance_sub_idx ON serial.issuance (subscription);
-CREATE INDEX serial_issuance_caption_and_pattern_idx ON serial.issuance (caption_and_pattern);
-CREATE INDEX serial_issuance_date_published_idx ON serial.issuance (date_published);
-CREATE UNIQUE INDEX unit_barcode_key ON serial.unit (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
-CREATE INDEX unit_cn_idx ON serial.unit (call_number);
-CREATE INDEX unit_avail_cn_idx ON serial.unit (call_number);
-CREATE INDEX unit_creator_idx  ON serial.unit ( creator );
-CREATE INDEX unit_editor_idx   ON serial.unit ( editor );
-CREATE INDEX serial_item_stream_idx ON serial.item (stream);
-CREATE INDEX serial_item_issuance_idx ON serial.item (issuance);
-CREATE INDEX serial_item_unit_idx ON serial.item (unit);
-CREATE INDEX serial_item_uri_idx ON serial.item (uri);
-CREATE INDEX serial_item_date_received_idx ON serial.item (date_received);
-CREATE INDEX serial_item_status_idx ON serial.item (status);
-CREATE INDEX serial_item_note_item_idx ON serial.item_note (item);
-CREATE INDEX serial_basic_summary_dist_idx ON serial.basic_summary (distribution);
-CREATE INDEX serial_supplement_summary_dist_idx ON serial.supplement_summary (distribution);
-CREATE INDEX serial_index_summary_dist_idx ON serial.index_summary (distribution);
+CREATE INDEX acq_picklist_owner_idx   ON acq.picklist ( owner );
+CREATE INDEX acq_picklist_creator_idx ON acq.picklist ( creator );
+CREATE INDEX acq_picklist_editor_idx  ON acq.picklist ( editor );
+CREATE INDEX acq_po_note_creator_idx  ON acq.po_note ( creator );
+CREATE INDEX acq_po_note_editor_idx   ON acq.po_note ( editor );
+CREATE INDEX fund_alloc_allocator_idx ON acq.fund_allocation ( allocator );
+CREATE INDEX li_creator_idx   ON acq.lineitem ( creator );
+CREATE INDEX li_editor_idx    ON acq.lineitem ( editor );
+CREATE INDEX li_selector_idx  ON acq.lineitem ( selector );
+CREATE INDEX li_note_creator_idx  ON acq.lineitem_note ( creator );
+CREATE INDEX li_note_editor_idx   ON acq.lineitem_note ( editor );
+CREATE INDEX li_usr_attr_def_usr_idx  ON acq.lineitem_usr_attr_definition ( usr );
+CREATE INDEX po_editor_idx   ON acq.purchase_order ( editor );
+CREATE INDEX po_creator_idx  ON acq.purchase_order ( creator );
+CREATE INDEX acq_po_org_name_order_date_idx ON acq.purchase_order( ordering_agency, name, order_date );
+CREATE INDEX action_in_house_use_staff_idx  ON action.in_house_use ( staff );
+CREATE INDEX action_non_cat_circ_patron_idx ON action.non_cataloged_circulation ( patron );
+CREATE INDEX action_non_cat_circ_staff_idx  ON action.non_cataloged_circulation ( staff );
+CREATE INDEX action_survey_response_usr_idx ON action.survey_response ( usr );
+CREATE INDEX ahn_notify_staff_idx           ON action.hold_notification ( notify_staff );
+CREATE INDEX circ_all_usr_idx               ON action.circulation ( usr );
+CREATE INDEX circ_circ_staff_idx            ON action.circulation ( circ_staff );
+CREATE INDEX circ_checkin_staff_idx         ON action.circulation ( checkin_staff );
+CREATE INDEX hold_request_fulfillment_staff_idx ON action.hold_request ( fulfillment_staff );
+CREATE INDEX hold_request_requestor_idx     ON action.hold_request ( requestor );
+CREATE INDEX non_cat_in_house_use_staff_idx ON action.non_cat_in_house_use ( staff );
+CREATE INDEX actor_usr_note_creator_idx     ON actor.usr_note ( creator );
+CREATE INDEX actor_usr_standing_penalty_staff_idx ON actor.usr_standing_penalty ( staff );
+CREATE INDEX usr_org_unit_opt_in_staff_idx  ON actor.usr_org_unit_opt_in ( staff );
+CREATE INDEX asset_call_number_note_creator_idx ON asset.call_number_note ( creator );
+CREATE INDEX asset_copy_note_creator_idx    ON asset.copy_note ( creator );
+CREATE INDEX cp_creator_idx                 ON asset.copy ( creator );
+CREATE INDEX cp_editor_idx                  ON asset.copy ( editor );
 
 CREATE INDEX actor_card_barcode_lower_idx ON actor.card (lower(barcode));
 
-\qecho if the following CREATE INDEX fails, It will be necessary to do some
+DROP INDEX IF EXISTS authority.unique_by_heading_and_thesaurus;
+
+\qecho If the following CREATE INDEX fails, It will be necessary to do some
 \qecho data cleanup as described in the comments.
 
+CREATE UNIQUE INDEX unique_by_heading_and_thesaurus
+    ON authority.record_entry (authority.normalize_heading(marc))
+	WHERE deleted IS FALSE or deleted = FALSE;
+
 -- If the unique index fails, uncomment the following to create
 -- a regular index that will help find the duplicates in a hurry:
 --CREATE INDEX by_heading_and_thesaurus

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/110.hold_matrix.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/110.hold_matrix.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/110.hold_matrix.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -31,6 +31,7 @@
 CREATE TABLE config.hold_matrix_matchpoint (
     id                      SERIAL    PRIMARY KEY,
     active                  BOOL    NOT NULL DEFAULT TRUE,
+    strict_ou_match         BOOL    NOT NULL DEFAULT FALSE,
     user_home_ou            INT        REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,    -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
     request_ou              INT        REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,    -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
     pickup_ou               INT        REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,    -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
@@ -57,7 +58,6 @@
 CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
 DECLARE
     current_requestor_group    permission.grp_tree%ROWTYPE;
-    root_ou            actor.org_unit%ROWTYPE;
     requestor_object    actor.usr%ROWTYPE;
     user_object        actor.usr%ROWTYPE;
     item_object        asset.copy%ROWTYPE;
@@ -69,7 +69,6 @@
     current_mp        config.hold_matrix_matchpoint%ROWTYPE;
     matchpoint        config.hold_matrix_matchpoint%ROWTYPE;
 BEGIN
-    SELECT INTO root_ou * FROM actor.org_unit WHERE parent_ou IS NULL;
     SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
     SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
     SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
@@ -97,7 +96,11 @@
                     CASE WHEN m.marc_vr_format    IS NOT NULL THEN 2 ELSE 0 END +
                     CASE WHEN m.ref_flag        IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
 
-            current_mp_weight := 5.0;
+            IF NOT current_mp.strict_ou_match THEN
+                current_mp_weight := 5.0;
+            ELSE
+                current_mp_weight := 0.0;
+            END IF;
 
             IF current_mp.circ_modifier IS NOT NULL THEN
                 CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
@@ -129,28 +132,48 @@
 
 
             -- caclulate the rule match weight
-            IF current_mp.item_owning_ou IS NOT NULL AND current_mp.item_owning_ou <> root_ou.id THEN
-                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+            IF current_mp.item_owning_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.item_owning_ou = item_cn_object.owning_lib THEN 1.0 ELSE 0.0 END;
+                END IF;
                 current_mp_weight := current_mp_weight - tmp_weight;
             END IF; 
 
-            IF current_mp.item_circ_ou IS NOT NULL AND current_mp.item_circ_ou <> root_ou.id THEN
-                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+            IF current_mp.item_circ_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.item_circ_ou = item_object.circ_lib THEN 1.0 ELSE 0.0 END;
+                END IF;
                 current_mp_weight := current_mp_weight - tmp_weight;
             END IF; 
 
-            IF current_mp.pickup_ou IS NOT NULL AND current_mp.pickup_ou <> root_ou.id THEN
-                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+            IF current_mp.pickup_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.pickup_ou = pickiup_ou THEN 1.0 ELSE 0.0 END;
+                END IF;
                 current_mp_weight := current_mp_weight - tmp_weight;
             END IF; 
 
-            IF current_mp.request_ou IS NOT NULL AND current_mp.request_ou <> root_ou.id THEN
-                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+            IF current_mp.request_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.request_ou = request_ou THEN 1.0 ELSE 0.0 END;
+                END IF;
                 current_mp_weight := current_mp_weight - tmp_weight;
             END IF; 
 
-            IF current_mp.user_home_ou IS NOT NULL AND current_mp.user_home_ou <> root_ou.id THEN
-                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+            IF current_mp.user_home_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.user_home_ou = user_object.home_ou THEN 1.0 ELSE 0.0 END;
+                END IF;
                 current_mp_weight := current_mp_weight - tmp_weight;
             END IF; 
 
@@ -172,7 +195,7 @@
 $func$ LANGUAGE plpgsql;
 
 
-CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
 DECLARE
     matchpoint_id        INT;
     user_object        actor.usr%ROWTYPE;
@@ -306,7 +329,7 @@
         END LOOP;
     END IF;
 
-    IF hold_test.max_holds IS NOT NULL THEN
+    IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
         SELECT    INTO hold_count COUNT(*)
           FROM    action.hold_request
           WHERE    usr = match_user
@@ -349,5 +372,13 @@
 END;
 $func$ LANGUAGE plpgsql;
 
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+    SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, FALSE );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION action.hold_retarget_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+    SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, TRUE );
+$func$ LANGUAGE SQL;
+
 COMMIT;
 

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/200.schema.acq.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/200.schema.acq.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/200.schema.acq.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -482,7 +482,7 @@
 	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) DEFERRABLE INITIALLY DEFERRED,
+	eg_bib_id           BIGINT                      REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
 	source_label        TEXT,
 	state               TEXT                        NOT NULL DEFAULT 'new',
 	cancel_reason       INT                         REFERENCES acq.cancel_reason( id )

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/950.data.seed-values.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/950.data.seed-values.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/950.data.seed-values.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1390,6 +1390,15 @@
     ,(399, 'ADMIN_SERIAL_DISTRIBUTION', oils_i18n_gettext(399, 'Create/update/delete serial distribution objects', 'ppl', 'description'))
     ,(400, 'ADMIN_SERIAL_STREAM', oils_i18n_gettext(400, 'Create/update/delete serial stream objects', 'ppl', 'description'))
     ,(401, 'RECEIVE_SERIAL', oils_i18n_gettext(401, 'Receive serial items', 'ppl', 'description'))
+    ,(402, 'ADMIN_ACQ_DISTRIB_FORMULA', oils_i18n_gettext(402, 'Create/update/delete distribution formulae', 'ppl', 'description'))
+    ,(403, 'ADMIN_ACQ_CLAIM', oils_i18n_gettext(403, 'Create/update/delete acquisitions claims', 'ppl', 'description'))
+    ,(404, 'ADMIN_ACQ_CLAIM_EVENT_TYPE', oils_i18n_gettext(404, 'Create/update/delete acquisitions claim event types', 'ppl', 'description'))
+    ,(405, 'ADMIN_ACQ_CLAIM_TYPE', oils_i18n_gettext(405, 'Create/update/delete acquisitions claim types', 'ppl', 'description'))
+    ,(406, 'ADMIN_ACQ_FISCAL_YEAR', oils_i18n_gettext(406, 'Create/update/delete acquisitions fiscal years', 'ppl', 'description'))
+    ,(407, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT', oils_i18n_gettext(407, 'Create/update/delete acquisitions fund allocation percentages', 'ppl', 'description'))
+    ,(408, 'ADMIN_ACQ_FUND_TAG', oils_i18n_gettext(408, 'Create/update/delete acquisitions fund tags', 'ppl', 'description'))
+    ,(409, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT', oils_i18n_gettext(409, 'Create/update/delete acquisitions lineitem alert text', 'ppl', 'description'))
+
 ;
 
 
@@ -2336,6 +2345,7 @@
 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('staff_client', oils_i18n_gettext('staff_client', 'General Staff Client container', 'cbrebt', 'label'));
 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('bookbag', oils_i18n_gettext('bookbag', 'Book Bag', 'cbrebt', 'label'));
 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('reading_list', oils_i18n_gettext('reading_list', 'Reading List', 'cbrebt', 'label'));
+INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('template_merge',oils_i18n_gettext('template_merge','Template Merge Container', 'cbrebt', 'label'));
 
 INSERT INTO container.user_bucket_type (code,label) VALUES ('misc', oils_i18n_gettext('misc', 'Miscellaneous', 'cubt', 'label'));
 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks', oils_i18n_gettext('folks', 'Friends', 'cubt', 'label'));
@@ -4872,8 +4882,8 @@
         <tbody>
         [% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
             [% 
-                IF copy.eg_copy_id;
-                    SET copy = copy.eg_copy_id;
+                IF detail.eg_copy_id;
+                    SET copy = detail.eg_copy_id;
                     SET cn_label = copy.call_number.label;
                 ELSE; 
                     SET copy = detail; 
@@ -4940,9 +4950,19 @@
 
 
 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, cleanup_success, cleanup_failure, delay, delay_field, group_field, template) 
-    VALUES (23, true, 1, 'PO JEDI', 'acqpo.activated', 'Acq::PurchaseOrderEDIRequired', 'GeneratePurchaseOrderJEDI', NULL, NULL, '00:05:00', NULL, NULL,
+    VALUES (23, true, 1, 'PO JEDI', 'acqpo.activated', 'Acq::PurchaseOrderEDIRequired', 'GeneratePurchaseOrderJEDI', NULL, NULL, '00:00:00', NULL, NULL,
 $$[%- USE date -%]
-[%# start JEDI document -%]
+[%# start JEDI document 
+  # Vendor specific kludges:
+  # BT      - vendcode goes to NAD/BY *suffix*  w/ 91 qualifier
+  # INGRAM  - vendcode goes to NAD/BY *segment* w/ 91 qualifier (separately)
+  # BRODART - vendcode goes to FTX segment (lineitem level)
+-%]
+[%- 
+IF target.provider.edi_default.vendcode && target.provider.code == 'BRODART';
+    xtra_ftx = target.provider.edi_default.vendcode;
+END;
+-%]
 [%- BLOCK big_block -%]
 {
    "recipient":"[% target.provider.san %]",
@@ -4951,23 +4971,27 @@
      "ORDERS":[ "order", {
         "po_number":[% target.id %],
         "date":"[% date.format(date.now, '%Y%m%d') %]",
-        "buyer":[{
-            [%- IF target.provider.edi_default.vendcode -%]
-                "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]", 
-                "id-qualifier": 91
+        "buyer":[
+            [%   IF   target.provider.edi_default.vendcode && (target.provider.code == 'BT' || target.provider.name.match('(?i)^BAKER & TAYLOR'))  -%]
+                {"id-qualifier": 91, "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]"}
+            [%- ELSIF target.provider.edi_default.vendcode && target.provider.code == 'INGRAM' -%]
+                {"id":"[% target.ordering_agency.mailing_address.san %]"},
+                {"id-qualifier": 91, "id":"[% target.provider.edi_default.vendcode %]"}
             [%- ELSE -%]
-                "id":"[% target.ordering_agency.mailing_address.san %]"
-            [%- END  -%]
-        }],
-        "vendor":[ 
+                {"id":"[% target.ordering_agency.mailing_address.san %]"}
+            [%- END -%]
+        ],
+        "vendor":[
             [%- # target.provider.name (target.provider.id) -%]
             "[% target.provider.san %]",
             {"id-qualifier": 92, "id":"[% target.provider.id %]"}
         ],
         "currency":"[% target.provider.currency_type %]",
+                
         "items":[
-        [% FOR li IN target.lineitems %]
+        [%- FOR li IN target.lineitems %]
         {
+            "line_index":"[% li.id %]",
             "identifiers":[   [%-# li.isbns = helpers.get_li_isbns(li.attributes) %]
             [% FOR isbn IN helpers.get_li_isbns(li.attributes) -%]
                 [% IF isbn.length == 13 -%]
@@ -4976,23 +5000,35 @@
                 {"id-qualifier":"IB","id":"[% isbn %]"},
                 [%- END %]
             [% END %]
-                {"id-qualifier":"SA","id":"[% li.id %]"}
+                {"id-qualifier":"IN","id":"[% li.id %]"}
             ],
             "price":[% li.estimated_unit_price || '0.00' %],
             "desc":[
-                {"BTI":"[% helpers.get_li_attr('title',     '', li.attributes) %]"}, 
+                {"BTI":"[% helpers.get_li_attr('title',     '', li.attributes) %]"},
                 {"BPU":"[% helpers.get_li_attr('publisher', '', li.attributes) %]"},
                 {"BPD":"[% helpers.get_li_attr('pubdate',   '', li.attributes) %]"},
                 {"BPH":"[% helpers.get_li_attr('pagination','', li.attributes) %]"}
             ],
+            [%- ftx_vals = []; 
+                FOR note IN li.lineitem_notes; 
+                    NEXT UNLESS note.vendor_public == 't'; 
+                    ftx_vals.push(note.value); 
+                END; 
+                IF xtra_ftx;           ftx_vals.unshift(xtra_ftx); END; 
+                IF ftx_vals.size == 0; ftx_vals.unshift('');       END;  # BT needs FTX+LIN for every LI, even if it is an empty one
+            -%]
+
+            "free-text":[ 
+                [% FOR note IN ftx_vals -%] "[% note %]"[% UNLESS loop.last %], [% END %][% END %] 
+            ],            
             "quantity":[% li.lineitem_details.size %]
         }[% UNLESS loop.last %],[% END %]
         [%-# TODO: lineitem details (later) -%]
         [% END %]
         ],
         "line_items":[% target.lineitems.size %]
-     }]  [% # close ORDERS array %]
-   }]    [% # close  body  array %]
+     }]  [%# close ORDERS array %]
+   }]    [%# close  body  array %]
 }
 [% END %]
 [% tempo = PROCESS big_block; helpers.escape_json(tempo) %]

Modified: branches/serials-integration/Open-ILS/src/sql/Pg/reporter-schema.sql
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/reporter-schema.sql	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/reporter-schema.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -305,9 +305,11 @@
 	CASE
 		WHEN hold_type = 'T'
 			THEN target
+		WHEN hold_type = 'I'
+			THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = ahr.target)
 		WHEN hold_type = 'V'
 			THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
-		WHEN hold_type = 'C'
+		WHEN hold_type IN ('C','R','F')
 			THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
 		WHEN hold_type = 'M'
 			THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0414.schema.call-number-upd-ins-trigger.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0414.schema.call-number-upd-ins-trigger.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0414.schema.call-number-upd-ins-trigger.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0414.schema.call-number-upd-ins-trigger.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,13 @@
+BEGIN;
+
+-- Adding a trigger.  Upgrade # 0364 created the trigger function but not
+-- the trigger itself.  However the base install script 040.schema.asset.sql
+-- creates both the function and the trigger.
+
+INSERT INTO config.upgrade_log (version) VALUES ('0414'); -- Scott McKellar
+
+CREATE TRIGGER asset_label_sortkey_trigger
+    BEFORE UPDATE OR INSERT ON asset.call_number
+    FOR EACH ROW EXECUTE PROCEDURE asset.label_normalizer();
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0415.schema.rename-field-class-fkey.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0415.schema.rename-field-class-fkey.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0415.schema.rename-field-class-fkey.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0415.schema.rename-field-class-fkey.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,22 @@
+-- Dropping and recreating a foreign key constraint for config.metabib_field,
+-- in order to change its name.  WHen this foreign key was first introduced,
+-- the upgrade script gave it one name and the base install script gave it
+-- a different name.  Here we bring the names into sync.
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0415'); -- Scott McKellar
+
+\qecho Dropping and recreating a foreign key in order to change its name.
+\qecho If the DROP fails because the constraint doesn't exist under the old
+\qecho name, or the ADD fails because it already exists under the new name,
+\qecho then ignore the failure.
+
+ALTER TABLE config.metabib_field
+	DROP CONSTRAINT field_class_fkey;
+
+ALTER TABLE config.metabib_field
+	ADD CONSTRAINT metabib_field_field_class_fkey
+	FOREIGN KEY (field_class) REFERENCES config.metabib_class(name);
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0416.schema.rename-recuring-idx.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0416.schema.rename-recuring-idx.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0416.schema.rename-recuring-idx.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0416.schema.rename-recuring-idx.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,11 @@
+INSERT INTO config.upgrade_log (version) VALUES ('0416'); -- Scott McKellar
+
+\qecho No transaction.  Renaming two indexes to correct spelling.
+\qecho If either change fails, then the index was probably created
+\qecho correctly in the first place; ignore the failure.
+
+ALTER INDEX config.rule_recuring_fine_name_key
+	RENAME TO rule_recurring_fine_name_key;
+
+ALTER INDEX config.rule_recuring_fine_pkey
+	RENAME TO rule_recurring_fine_pkey;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0417.schema.acq.drop-lineitem-item-count.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0417.schema.acq.drop-lineitem-item-count.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0417.schema.acq.drop-lineitem-item-count.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0417.schema.acq.drop-lineitem-item-count.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,23 @@
+-- Drop the never-used column item_count from acq.lineitem.
+-- Drop it also from the associated history table, and rebuild
+-- the function that maintains it.  Finally, rebuild the
+-- associated lifecycle view.  
+
+-- Apply to trunk only; this column never existed in 2.0.
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0417'); -- Scott McKellar
+
+-- Have to drop the view first, because it's a dependent
+DROP VIEW IF EXISTS acq.acq_lineitem_lifecycle;
+
+ALTER TABLE acq.lineitem DROP COLUMN item_count;
+
+ALTER TABLE acq.acq_lineitem_history DROP COLUMN item_count;
+
+SELECT acq.create_acq_func( 'acq', 'lineitem' );
+
+SELECT acq.create_acq_lifecycle( 'acq', 'lineitem' );
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0418.function.action.fix-purge-circ.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0418.function.action.fix-purge-circ.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0418.function.action.fix-purge-circ.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0418.function.action.fix-purge-circ.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,99 @@
+-- Apply in an update script some fixes that were previously applied only
+-- to the base installation script 090.schema.action.sql.
+
+-- Also fix a typo: INTEVAL -> INTERVAL
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0418'); -- Scott McKellar
+
+CREATE OR REPLACE FUNCTION action.purge_circulations () RETURNS INT AS $func$
+DECLARE
+    usr_keep_age    actor.usr_setting%ROWTYPE;
+    usr_keep_start  actor.usr_setting%ROWTYPE;
+    org_keep_age    INTERVAL;
+    org_keep_count  INT;
+
+    keep_age        INTERVAL;
+
+    target_acp      RECORD;
+    circ_chain_head action.circulation%ROWTYPE;
+    circ_chain_tail action.circulation%ROWTYPE;
+
+    purge_position  INT;
+    count_purged    INT;
+BEGIN
+
+    count_purged := 0;
+
+    SELECT value::INTERVAL INTO org_keep_age FROM config.global_flag WHERE name = 'history.circ.retention_age' AND enabled;
+
+    SELECT value::INT INTO org_keep_count FROM config.global_flag WHERE name = 'history.circ.retention_count' AND enabled;
+    IF org_keep_count IS NULL THEN
+        RETURN count_purged; -- Gimme a count to keep, or I keep them all, forever
+    END IF;
+
+    -- First, find copies with more than keep_count non-renewal circs
+    FOR target_acp IN
+        SELECT  target_copy,
+                COUNT(*) AS total_real_circs
+          FROM  action.circulation
+          WHERE parent_circ IS NULL
+                AND xact_finish IS NOT NULL
+          GROUP BY target_copy
+          HAVING COUNT(*) > org_keep_count
+    LOOP
+        purge_position := 0;
+        -- And, for those, select circs that are finished and older than keep_age
+        FOR circ_chain_head IN
+            SELECT  *
+              FROM  action.circulation
+              WHERE target_copy = target_acp.target_copy
+                    AND parent_circ IS NULL
+              ORDER BY xact_start
+        LOOP
+
+            -- Stop once we've purged enough circs to hit org_keep_count
+            EXIT WHEN target_acp.total_real_circs - purge_position <= org_keep_count;
+
+            SELECT * INTO circ_chain_tail FROM action.circ_chain(circ_chain_head.id) ORDER BY xact_start DESC LIMIT 1;
+            EXIT WHEN circ_chain_tail.xact_finish IS NULL;
+
+            -- Now get the user settings, if any, to block purging if the user wants to keep more circs
+            usr_keep_age.value := NULL;
+            SELECT * INTO usr_keep_age FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_age';
+
+            usr_keep_start.value := NULL;
+            SELECT * INTO usr_keep_start FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_start';
+
+            IF usr_keep_age.value IS NOT NULL AND usr_keep_start.value IS NOT NULL THEN
+                IF oils_json_to_text(usr_keep_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ) THEN
+                    keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
+                ELSE
+                    keep_age := oils_json_to_text(usr_keep_age.value)::INTERVAL;
+                END IF;
+            ELSIF usr_keep_start.value IS NOT NULL THEN
+                keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
+            ELSE
+                keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTERVAL );
+            END IF;
+
+            EXIT WHEN AGE(NOW(), circ_chain_tail.xact_finish) < keep_age;
+
+            -- We've passed the purging tests, purge the circ chain starting at the end
+            DELETE FROM action.circulation WHERE id = circ_chain_tail.id;
+            WHILE circ_chain_tail.parent_circ IS NOT NULL LOOP
+                SELECT * INTO circ_chain_tail FROM action.circulation WHERE id = circ_chain_tail.parent_circ;
+                DELETE FROM action.circulation WHERE id = circ_chain_tail.id;
+            END LOOP;
+
+            count_purged := count_purged + 1;
+            purge_position := purge_position + 1;
+
+        END LOOP;
+    END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0419.schema.hold_record_view.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0419.schema.hold_record_view.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0419.schema.hold_record_view.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0419.schema.hold_record_view.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,23 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0419'); -- miker
+
+CREATE OR REPLACE VIEW reporter.hold_request_record AS
+SELECT  id,
+    target,
+    hold_type,
+    CASE
+        WHEN hold_type = 'T'
+            THEN target
+        WHEN hold_type = 'I'
+            THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = ahr.target)
+        WHEN hold_type = 'V'
+            THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
+        WHEN hold_type IN ('C','R','F')
+            THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
+        WHEN hold_type = 'M'
+            THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)
+    END AS bib_record
+  FROM  action.hold_request ahr;
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0420.schema.premunge_dates.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0420.schema.premunge_dates.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0420.schema.premunge_dates.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0420.schema.premunge_dates.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,40 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0420'); -- miker
+
+CREATE OR REPLACE FUNCTION metabib.reingest_metabib_rec_descriptor( bib_id BIGINT ) RETURNS VOID AS $func$
+BEGIN
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        DELETE FROM metabib.rec_descriptor WHERE record = bib_id;
+    END IF;
+    INSERT INTO metabib.rec_descriptor (record, item_type, item_form, bib_level, control_type, enc_level, audience, lit_form, type_mat, cat_form, pub_status, item_lang, vr_format, date1, date2)
+        SELECT  bib_id,
+                biblio.marc21_extract_fixed_field( bib_id, 'Type' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'Form' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'BLvl' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'Ctrl' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'ELvl' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'Audn' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'LitF' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'TMat' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'Desc' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'DtSt' ),
+                biblio.marc21_extract_fixed_field( bib_id, 'Lang' ),
+                (   SELECT  v.value
+                      FROM  biblio.marc21_physical_characteristics( bib_id) p
+                            JOIN config.marc21_physical_characteristic_subfield_map s ON (s.id = p.subfield)
+                            JOIN config.marc21_physical_characteristic_value_map v ON (v.id = p.value)
+                      WHERE p.ptype = 'v' AND s.subfield = 'e'    ),
+                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date1'), ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
+                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date2'), ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+UPDATE  metabib.rec_descriptor
+  SET   date1=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date1, ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
+        date2=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date2, ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0421.schema.embiggen-ints.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0421.schema.embiggen-ints.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0421.schema.embiggen-ints.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0421.schema.embiggen-ints.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,44 @@
+-- 1. Turn some ints into bigints.
+
+-- 2. Rename a constraint for consistency and accuracy (currently it may
+-- have either of two different names).
+
+\qecho One of the following DROPs will fail, so we do them
+\qecho both outside of a transaction.  Ignore the failure.
+
+ALTER TABLE booking.resource_type
+	DROP CONSTRAINT brt_name_or_record_once_per_owner;
+
+ALTER TABLE booking.resource_type
+	DROP CONSTRAINT brt_name_once_per_owner;
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0421'); -- Scott McKellar
+
+ALTER TABLE booking.resource_type
+	ALTER COLUMN record SET DATA TYPE bigint,
+	ADD CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record);
+
+ALTER TABLE container.biblio_record_entry_bucket_item
+	ALTER COLUMN target_biblio_record_entry SET DATA TYPE bigint;
+
+-- Before we can embiggen the next one, we must drop a view
+-- that depends on it (and recreate it later)
+
+DROP VIEW IF EXISTS acq.acq_lineitem_lifecycle;
+
+ALTER TABLE acq.lineitem
+	ALTER COLUMN eg_bib_id SET DATA TYPE bigint;
+
+-- Recreate the view
+
+SELECT acq.create_acq_lifecycle( 'acq', 'lineitem' );
+
+ALTER TABLE vandelay.queued_bib_record
+	ALTER COLUMN imported_as SET DATA TYPE bigint;
+
+ALTER TABLE action.hold_copy_map
+	ALTER COLUMN id SET DATA TYPE bigint;
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0422.schema.acq.lineitem-history-bigint.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0422.schema.acq.lineitem-history-bigint.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0422.schema.acq.lineitem-history-bigint.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0422.schema.acq.lineitem-history-bigint.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,15 @@
+BEGIN;
+
+-- Turn an int into a bigint in acq.acq_lineitem_history, following up on
+-- a similar change to acq.lineitem
+
+INSERT INTO config.upgrade_log (version) VALUES ('0422'); -- Scott McKellar
+
+DROP VIEW IF EXISTS acq.acq_lineitem_lifecycle;
+
+ALTER TABLE acq.acq_lineitem_history
+	ALTER COLUMN eg_bib_id SET DATA TYPE bigint;
+
+SELECT acq.create_acq_lifecycle( 'acq', 'lineitem' );
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0423.schema.support-null-function-in-xpath_table.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0423.schema.support-null-function-in-xpath_table.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0423.schema.support-null-function-in-xpath_table.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0423.schema.support-null-function-in-xpath_table.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,67 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0423'); --miker
+
+CREATE OR REPLACE FUNCTION oils_xpath_table ( key TEXT, document_field TEXT, relation_name TEXT, xpaths TEXT, criteria TEXT ) RETURNS SETOF RECORD AS $func$
+DECLARE
+    xpath_list  TEXT[];
+    select_list TEXT[];
+    where_list  TEXT[];
+    q           TEXT;
+    out_record  RECORD;
+    empty_test  RECORD;
+BEGIN
+    xpath_list := STRING_TO_ARRAY( xpaths, '|' );
+ 
+    select_list := ARRAY_APPEND( select_list, key || '::INT AS key' );
+ 
+    FOR i IN 1 .. ARRAY_UPPER(xpath_list,1) LOOP
+        IF xpath_list[i] = 'null()' THEN
+            select_list := ARRAY_APPEND( select_list, 'NULL::TEXT AS c_' || i );
+        ELSE
+            select_list := ARRAY_APPEND(
+                select_list,
+                $sel$
+                EXPLODE_ARRAY(
+                    COALESCE(
+                        NULLIF(
+                            oils_xpath(
+                                $sel$ ||
+                                    quote_literal(
+                                        CASE
+                                            WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
+                                            ELSE xpath_list[i] || '//text()'
+                                        END
+                                    ) ||
+                                $sel$,
+                                $sel$ || document_field || $sel$
+                            ),
+                           '{}'::TEXT[]
+                        ),
+                        '{NULL}'::TEXT[]
+                    )
+                ) AS c_$sel$ || i
+            );
+            where_list := ARRAY_APPEND(
+                where_list,
+                'c_' || i || ' IS NOT NULL'
+            );
+        END IF;
+    END LOOP;
+ 
+    q := $q$
+SELECT * FROM (
+    SELECT $q$ || ARRAY_TO_STRING( select_list, ', ' ) || $q$ FROM $q$ || relation_name || $q$ WHERE ($q$ || criteria || $q$)
+)x WHERE $q$ || ARRAY_TO_STRING( where_list, ' AND ' );
+    -- RAISE NOTICE 'query: %', q;
+ 
+    FOR out_record IN EXECUTE q LOOP
+        RETURN NEXT out_record;
+    END LOOP;
+ 
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+COMMIT;
+

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0424.schema.circ_due_date_trigger.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0424.schema.circ_due_date_trigger.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0424.schema.circ_due_date_trigger.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0424.schema.circ_due_date_trigger.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,9 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0424'); -- dbs
+
+DROP TRIGGER push_due_date_tgr ON action.circulation;
+
+CREATE TRIGGER push_due_date_tgr BEFORE INSERT OR UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0425.schema.perm-grp-tree-hold-priority.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0425.schema.perm-grp-tree-hold-priority.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0425.schema.perm-grp-tree-hold-priority.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0425.schema.perm-grp-tree-hold-priority.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,8 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0425'); -- Scott McKellar
+
+ALTER TABLE permission.grp_tree
+	ADD COLUMN hold_priority INT NOT NULL DEFAULT 0;
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0426.data.perm-list.misc-acq.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0426.data.perm-list.misc-acq.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0426.data.perm-list.misc-acq.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0426.data.perm-list.misc-acq.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,16 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0426'); -- senator
+
+INSERT INTO permission.perm_list VALUES
+    (402, 'ADMIN_ACQ_DISTRIB_FORMULA', oils_i18n_gettext(402, 'Create/update/delete distribution formulae', 'ppl', 'description'))
+    ,(403, 'ADMIN_ACQ_CLAIM', oils_i18n_gettext(403, 'Create/update/delete acquisitions claims', 'ppl', 'description'))
+    ,(404, 'ADMIN_ACQ_CLAIM_EVENT_TYPE', oils_i18n_gettext(404, 'Create/update/delete acquisitions claim event types', 'ppl', 'description'))
+    ,(405, 'ADMIN_ACQ_CLAIM_TYPE', oils_i18n_gettext(405, 'Create/update/delete acquisitions claim types', 'ppl', 'description'))
+    ,(406, 'ADMIN_ACQ_FISCAL_YEAR', oils_i18n_gettext(406, 'Create/update/delete acquisitions fiscal years', 'ppl', 'description'))
+    ,(407, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT', oils_i18n_gettext(407, 'Create/update/delete acquisitions fund allocation percentages', 'ppl', 'description'))
+    ,(408, 'ADMIN_ACQ_FUND_TAG', oils_i18n_gettext(408, 'Create/update/delete acquisitions fund tags', 'ppl', 'description'))
+    ,(409, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT', oils_i18n_gettext(409, 'Create/update/delete acquisitions lineitem alert text', 'ppl', 'description'))
+;
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0427.schema.hold_matrix_root_ou.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0427.schema.hold_matrix_root_ou.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0427.schema.hold_matrix_root_ou.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0427.schema.hold_matrix_root_ou.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,120 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0427'); -- gmc
+
+CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
+DECLARE
+    current_requestor_group    permission.grp_tree%ROWTYPE;
+    requestor_object    actor.usr%ROWTYPE;
+    user_object        actor.usr%ROWTYPE;
+    item_object        asset.copy%ROWTYPE;
+    item_cn_object        asset.call_number%ROWTYPE;
+    rec_descriptor        metabib.rec_descriptor%ROWTYPE;
+    current_mp_weight    FLOAT;
+    matchpoint_weight    FLOAT;
+    tmp_weight        FLOAT;
+    current_mp        config.hold_matrix_matchpoint%ROWTYPE;
+    matchpoint        config.hold_matrix_matchpoint%ROWTYPE;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+    SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
+    SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+    SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+    SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r WHERE r.record = item_cn_object.record;
+
+    PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
+
+    IF NOT FOUND THEN
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = requestor_object.profile;
+    ELSE
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = user_object.profile;
+    END IF;
+
+    LOOP 
+        -- for each potential matchpoint for this ou and group ...
+        FOR current_mp IN
+            SELECT    m.*
+              FROM    config.hold_matrix_matchpoint m
+              WHERE    m.requestor_grp = current_requestor_group.id AND m.active
+              ORDER BY    CASE WHEN m.circ_modifier    IS NOT NULL THEN 16 ELSE 0 END +
+                    CASE WHEN m.juvenile_flag    IS NOT NULL THEN 16 ELSE 0 END +
+                    CASE WHEN m.marc_type        IS NOT NULL THEN 8 ELSE 0 END +
+                    CASE WHEN m.marc_form        IS NOT NULL THEN 4 ELSE 0 END +
+                    CASE WHEN m.marc_vr_format    IS NOT NULL THEN 2 ELSE 0 END +
+                    CASE WHEN m.ref_flag        IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
+
+            current_mp_weight := 5.0;
+
+            IF current_mp.circ_modifier IS NOT NULL THEN
+                CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
+            END IF;
+
+            IF current_mp.marc_type IS NOT NULL THEN
+                IF item_object.circ_as_type IS NOT NULL THEN
+                    CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
+                ELSE
+                    CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
+                END IF;
+            END IF;
+
+            IF current_mp.marc_form IS NOT NULL THEN
+                CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
+            END IF;
+
+            IF current_mp.marc_vr_format IS NOT NULL THEN
+                CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
+            END IF;
+
+            IF current_mp.juvenile_flag IS NOT NULL THEN
+                CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
+            END IF;
+
+            IF current_mp.ref_flag IS NOT NULL THEN
+                CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
+            END IF;
+
+
+            -- caclulate the rule match weight
+            IF current_mp.item_owning_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.item_circ_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.pickup_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.request_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.user_home_ou IS NOT NULL THEN
+                SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            -- set the matchpoint if we found the best one
+            IF matchpoint_weight IS NULL OR matchpoint_weight > current_mp_weight THEN
+                matchpoint = current_mp;
+                matchpoint_weight = current_mp_weight;
+            END IF;
+
+        END LOOP;
+
+        EXIT WHEN current_requestor_group.parent IS NULL OR matchpoint.id IS NOT NULL;
+
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = current_requestor_group.parent;
+    END LOOP;
+
+    RETURN matchpoint.id;
+END;
+$func$ LANGUAGE plpgsql;
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0428.schema.hold_retarget.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0428.schema.hold_retarget.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0428.schema.hold_retarget.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0428.schema.hold_retarget.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,193 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0428'); -- miker
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
+DECLARE
+    matchpoint_id        INT;
+    user_object        actor.usr%ROWTYPE;
+    age_protect_object    config.rule_age_hold_protect%ROWTYPE;
+    standing_penalty    config.standing_penalty%ROWTYPE;
+    transit_range_ou_type    actor.org_unit_type%ROWTYPE;
+    transit_source        actor.org_unit%ROWTYPE;
+    item_object        asset.copy%ROWTYPE;
+    ou_skip              actor.org_unit_setting%ROWTYPE;
+    result            action.matrix_test_result;
+    hold_test        config.hold_matrix_matchpoint%ROWTYPE;
+    hold_count        INT;
+    hold_transit_prox    INT;
+    frozen_hold_count    INT;
+    context_org_list    INT[];
+    done            BOOL := FALSE;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+    SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
+
+    result.success := TRUE;
+
+    -- Fail if we couldn't find a user
+    IF user_object.id IS NULL THEN
+        result.fail_part := 'no_user';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+
+    -- Fail if we couldn't find a copy
+    IF item_object.id IS NULL THEN
+        result.fail_part := 'no_item';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
+    result.matchpoint := matchpoint_id;
+
+    SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
+
+    -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
+    IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
+        result.fail_part := 'circ.holds.target_skip_me';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    -- Fail if user is barred
+    IF user_object.barred IS TRUE THEN
+        result.fail_part := 'actor.usr.barred';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    -- Fail if we couldn't find any matchpoint (requires a default)
+    IF matchpoint_id IS NULL THEN
+        result.fail_part := 'no_matchpoint';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
+
+    IF hold_test.holdable IS FALSE THEN
+        result.fail_part := 'config.hold_matrix_test.holdable';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+    END IF;
+
+    IF hold_test.transit_range IS NOT NULL THEN
+        SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
+        IF hold_test.distance_is_from_owner THEN
+            SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number;
+        ELSE
+            SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
+        END IF;
+
+        PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
+
+        IF NOT FOUND THEN
+            result.fail_part := 'transit_range';
+            result.success := FALSE;
+            done := TRUE;
+            RETURN NEXT result;
+        END IF;
+    END IF;
+ 
+    IF NOT retargetting THEN
+        FOR standing_penalty IN
+            SELECT  DISTINCT csp.*
+              FROM  actor.usr_standing_penalty usp
+                    JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+              WHERE usr = match_user
+                    AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+                    AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+                    AND csp.block_list LIKE '%HOLD%' LOOP
+    
+            result.fail_part := standing_penalty.name;
+            result.success := FALSE;
+            done := TRUE;
+            RETURN NEXT result;
+        END LOOP;
+    
+        IF hold_test.stop_blocked_user IS TRUE THEN
+            FOR standing_penalty IN
+                SELECT  DISTINCT csp.*
+                  FROM  actor.usr_standing_penalty usp
+                        JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+                  WHERE usr = match_user
+                        AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+                        AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+                        AND csp.block_list LIKE '%CIRC%' LOOP
+        
+                result.fail_part := standing_penalty.name;
+                result.success := FALSE;
+                done := TRUE;
+                RETURN NEXT result;
+            END LOOP;
+        END IF;
+    
+        IF hold_test.max_holds IS NOT NULL THEN
+            SELECT    INTO hold_count COUNT(*)
+              FROM    action.hold_request
+              WHERE    usr = match_user
+                AND fulfillment_time IS NULL
+                AND cancel_time IS NULL
+                AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
+    
+            IF hold_count >= hold_test.max_holds THEN
+                result.fail_part := 'config.hold_matrix_test.max_holds';
+                result.success := FALSE;
+                done := TRUE;
+                RETURN NEXT result;
+            END IF;
+        END IF;
+    END IF;
+
+    IF item_object.age_protect IS NOT NULL THEN
+        SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
+
+        IF item_object.create_date + age_protect_object.age > NOW() THEN
+            IF hold_test.distance_is_from_owner THEN
+                SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
+            ELSE
+                SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
+            END IF;
+
+            IF hold_transit_prox > age_protect_object.prox THEN
+                result.fail_part := 'config.rule_age_hold_protect.prox';
+                result.success := FALSE;
+                done := TRUE;
+                RETURN NEXT result;
+            END IF;
+        END IF;
+    END IF;
+
+    IF NOT done THEN
+        RETURN NEXT result;
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+    SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, FALSE );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION action.hold_retarget_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+    SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, TRUE );
+$func$ LANGUAGE SQL;
+
+COMMIT;
+

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0429.noop-because-miker-skipped-it.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0429.noop-because-miker-skipped-it.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0429.noop-because-miker-skipped-it.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0429.noop-because-miker-skipped-it.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1 @@
+INSERT INTO config.upgrade_log (version) VALUES ('0429'); -- miker, for shame

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0430.schema.strict_ou_test.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0430.schema.strict_ou_test.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0430.schema.strict_ou_test.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0430.schema.strict_ou_test.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,143 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0430'); -- miker
+
+ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN strict_ou_match BOOL NOT NULL DEFAULT FALSE;
+
+CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
+DECLARE
+    current_requestor_group    permission.grp_tree%ROWTYPE;
+    requestor_object    actor.usr%ROWTYPE;
+    user_object        actor.usr%ROWTYPE;
+    item_object        asset.copy%ROWTYPE;
+    item_cn_object        asset.call_number%ROWTYPE;
+    rec_descriptor        metabib.rec_descriptor%ROWTYPE;
+    current_mp_weight    FLOAT;
+    matchpoint_weight    FLOAT;
+    tmp_weight        FLOAT;
+    current_mp        config.hold_matrix_matchpoint%ROWTYPE;
+    matchpoint        config.hold_matrix_matchpoint%ROWTYPE;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+    SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
+    SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+    SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+    SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r WHERE r.record = item_cn_object.record;
+
+    PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
+
+    IF NOT FOUND THEN
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = requestor_object.profile;
+    ELSE
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = user_object.profile;
+    END IF;
+
+    LOOP 
+        -- for each potential matchpoint for this ou and group ...
+        FOR current_mp IN
+            SELECT    m.*
+              FROM    config.hold_matrix_matchpoint m
+              WHERE    m.requestor_grp = current_requestor_group.id AND m.active
+              ORDER BY    CASE WHEN m.circ_modifier    IS NOT NULL THEN 16 ELSE 0 END +
+                    CASE WHEN m.juvenile_flag    IS NOT NULL THEN 16 ELSE 0 END +
+                    CASE WHEN m.marc_type        IS NOT NULL THEN 8 ELSE 0 END +
+                    CASE WHEN m.marc_form        IS NOT NULL THEN 4 ELSE 0 END +
+                    CASE WHEN m.marc_vr_format    IS NOT NULL THEN 2 ELSE 0 END +
+                    CASE WHEN m.ref_flag        IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
+
+            current_mp_weight := 5.0;
+
+            IF current_mp.circ_modifier IS NOT NULL THEN
+                CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
+            END IF;
+
+            IF current_mp.marc_type IS NOT NULL THEN
+                IF item_object.circ_as_type IS NOT NULL THEN
+                    CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
+                ELSE
+                    CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
+                END IF;
+            END IF;
+
+            IF current_mp.marc_form IS NOT NULL THEN
+                CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
+            END IF;
+
+            IF current_mp.marc_vr_format IS NOT NULL THEN
+                CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
+            END IF;
+
+            IF current_mp.juvenile_flag IS NOT NULL THEN
+                CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
+            END IF;
+
+            IF current_mp.ref_flag IS NOT NULL THEN
+                CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
+            END IF;
+
+
+            -- caclulate the rule match weight
+            IF current_mp.item_owning_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.item_owning_ou = item_cn_object.owning_lib THEN 1.0 ELSE 0.0 END;
+                END IF;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.item_circ_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.item_circ_ou = item_object.circ_lib THEN 1.0 ELSE 0.0 END;
+                END IF;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.pickup_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.pickup_ou = pickiup_ou THEN 1.0 ELSE 0.0 END;
+                END IF;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.request_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.request_ou = request_ou THEN 1.0 ELSE 0.0 END;
+                END IF;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            IF current_mp.user_home_ou IS NOT NULL THEN
+                IF NOT current_mp.strict_ou_match THEN
+                    SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+                ELSE
+                    tmp_weight := CASE WHEN current_mp.user_home_ou = user_object.home_ou THEN 1.0 ELSE 0.0 END;
+                END IF;
+                current_mp_weight := current_mp_weight - tmp_weight;
+            END IF; 
+
+            -- set the matchpoint if we found the best one
+            IF matchpoint_weight IS NULL OR matchpoint_weight > current_mp_weight THEN
+                matchpoint = current_mp;
+                matchpoint_weight = current_mp_weight;
+            END IF;
+
+        END LOOP;
+
+        EXIT WHEN current_requestor_group.parent IS NULL OR matchpoint.id IS NOT NULL;
+
+        SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = current_requestor_group.parent;
+    END LOOP;
+
+    RETURN matchpoint.id;
+END;
+$func$ LANGUAGE plpgsql;
+
+COMMIT;
+

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0431.schema.hold_retarget.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0431.schema.hold_retarget.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0431.schema.hold_retarget.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0431.schema.hold_retarget.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,183 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0431'); -- miker
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
+DECLARE
+    matchpoint_id        INT;
+    user_object        actor.usr%ROWTYPE;
+    age_protect_object    config.rule_age_hold_protect%ROWTYPE;
+    standing_penalty    config.standing_penalty%ROWTYPE;
+    transit_range_ou_type    actor.org_unit_type%ROWTYPE;
+    transit_source        actor.org_unit%ROWTYPE;
+    item_object        asset.copy%ROWTYPE;
+    ou_skip              actor.org_unit_setting%ROWTYPE;
+    result            action.matrix_test_result;
+    hold_test        config.hold_matrix_matchpoint%ROWTYPE;
+    hold_count        INT;
+    hold_transit_prox    INT;
+    frozen_hold_count    INT;
+    context_org_list    INT[];
+    done            BOOL := FALSE;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+    SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
+
+    result.success := TRUE;
+
+    -- Fail if we couldn't find a user
+    IF user_object.id IS NULL THEN
+        result.fail_part := 'no_user';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+
+    -- Fail if we couldn't find a copy
+    IF item_object.id IS NULL THEN
+        result.fail_part := 'no_item';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
+    result.matchpoint := matchpoint_id;
+
+    SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
+
+    -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
+    IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
+        result.fail_part := 'circ.holds.target_skip_me';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    -- Fail if user is barred
+    IF user_object.barred IS TRUE THEN
+        result.fail_part := 'actor.usr.barred';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    -- Fail if we couldn't find any matchpoint (requires a default)
+    IF matchpoint_id IS NULL THEN
+        result.fail_part := 'no_matchpoint';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+        RETURN;
+    END IF;
+
+    SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
+
+    IF hold_test.holdable IS FALSE THEN
+        result.fail_part := 'config.hold_matrix_test.holdable';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+    END IF;
+
+    IF hold_test.transit_range IS NOT NULL THEN
+        SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
+        IF hold_test.distance_is_from_owner THEN
+            SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number;
+        ELSE
+            SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
+        END IF;
+
+        PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
+
+        IF NOT FOUND THEN
+            result.fail_part := 'transit_range';
+            result.success := FALSE;
+            done := TRUE;
+            RETURN NEXT result;
+        END IF;
+    END IF;
+ 
+    FOR standing_penalty IN
+        SELECT  DISTINCT csp.*
+          FROM  actor.usr_standing_penalty usp
+                JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+          WHERE usr = match_user
+                AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+                AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+                AND csp.block_list LIKE '%HOLD%' LOOP
+
+        result.fail_part := standing_penalty.name;
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+    END LOOP;
+
+    IF hold_test.stop_blocked_user IS TRUE THEN
+        FOR standing_penalty IN
+            SELECT  DISTINCT csp.*
+              FROM  actor.usr_standing_penalty usp
+                    JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+              WHERE usr = match_user
+                    AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+                    AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+                    AND csp.block_list LIKE '%CIRC%' LOOP
+    
+            result.fail_part := standing_penalty.name;
+            result.success := FALSE;
+            done := TRUE;
+            RETURN NEXT result;
+        END LOOP;
+    END IF;
+
+    IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
+        SELECT    INTO hold_count COUNT(*)
+          FROM    action.hold_request
+          WHERE    usr = match_user
+            AND fulfillment_time IS NULL
+            AND cancel_time IS NULL
+            AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
+
+        IF hold_count >= hold_test.max_holds THEN
+            result.fail_part := 'config.hold_matrix_test.max_holds';
+            result.success := FALSE;
+            done := TRUE;
+            RETURN NEXT result;
+        END IF;
+    END IF;
+
+    IF item_object.age_protect IS NOT NULL THEN
+        SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
+
+        IF item_object.create_date + age_protect_object.age > NOW() THEN
+            IF hold_test.distance_is_from_owner THEN
+                SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
+            ELSE
+                SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
+            END IF;
+
+            IF hold_transit_prox > age_protect_object.prox THEN
+                result.fail_part := 'config.rule_age_hold_protect.prox';
+                result.success := FALSE;
+                done := TRUE;
+                RETURN NEXT result;
+            END IF;
+        END IF;
+    END IF;
+
+    IF NOT done THEN
+        RETURN NEXT result;
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE plpgsql;
+
+COMMIT;
+

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0432.schema.config_hard_due_date.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0432.schema.config_hard_due_date.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0432.schema.config_hard_due_date.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0432.schema.config_hard_due_date.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,16 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0432'); -- Scott McKellar
+
+ALTER TABLE config.rule_circ_duration
+	ADD COLUMN date_ceiling TIMESTAMPTZ;
+
+CREATE TABLE config.hard_due_date (
+	id              SERIAL      PRIMARY KEY,
+	duration_rule   INT         NOT NULL REFERENCES config.rule_circ_duration (id)
+	                            DEFERRABLE INITIALLY DEFERRED,
+	ceiling_date    TIMESTAMPTZ NOT NULL,
+	active_date     TIMESTAMPTZ NOT NULL
+);
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0433.edi_orders_template.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0433.edi_orders_template.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0433.edi_orders_template.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0433.edi_orders_template.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,91 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0433'); -- atz
+
+UPDATE action_trigger.event_definition SET delay='00:00:00', template=$$
+[%- USE date -%]
+[%# start JEDI document 
+  # Vendor specific kludges:
+  # BT      - vendcode goes to NAD/BY *suffix*  w/ 91 qualifier
+  # INGRAM  - vendcode goes to NAD/BY *segment* w/ 91 qualifier (separately)
+  # BRODART - vendcode goes to FTX segment (lineitem level)
+-%]
+[%- 
+IF target.provider.edi_default.vendcode && target.provider.code == 'BRODART';
+    xtra_ftx = target.provider.edi_default.vendcode;
+END;
+-%]
+[%- BLOCK big_block -%]
+{
+   "recipient":"[% target.provider.san %]",
+   "sender":"[% target.ordering_agency.mailing_address.san %]",
+   "body": [{
+     "ORDERS":[ "order", {
+        "po_number":[% target.id %],
+        "date":"[% date.format(date.now, '%Y%m%d') %]",
+        "buyer":[
+            [%   IF   target.provider.edi_default.vendcode && (target.provider.code == 'BT' || target.provider.name.match('(?i)^BAKER & TAYLOR'))  -%]
+                {"id-qualifier": 91, "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]"}
+            [%- ELSIF target.provider.edi_default.vendcode && target.provider.code == 'INGRAM' -%]
+                {"id":"[% target.ordering_agency.mailing_address.san %]"},
+                {"id-qualifier": 91, "id":"[% target.provider.edi_default.vendcode %]"}
+            [%- ELSE -%]
+                {"id":"[% target.ordering_agency.mailing_address.san %]"}
+            [%- END -%]
+        ],
+        "vendor":[
+            [%- # target.provider.name (target.provider.id) -%]
+            "[% target.provider.san %]",
+            {"id-qualifier": 92, "id":"[% target.provider.id %]"}
+        ],
+        "currency":"[% target.provider.currency_type %]",
+                
+        "items":[
+        [%- FOR li IN target.lineitems %]
+        {
+            "line_index":"[% li.id %]",
+            "identifiers":[   [%-# li.isbns = helpers.get_li_isbns(li.attributes) %]
+            [% FOR isbn IN helpers.get_li_isbns(li.attributes) -%]
+                [% IF isbn.length == 13 -%]
+                {"id-qualifier":"EN","id":"[% isbn %]"},
+                [% ELSE -%]
+                {"id-qualifier":"IB","id":"[% isbn %]"},
+                [%- END %]
+            [% END %]
+                {"id-qualifier":"IN","id":"[% li.id %]"}
+            ],
+            "price":[% li.estimated_unit_price || '0.00' %],
+            "desc":[
+                {"BTI":"[% helpers.get_li_attr('title',     '', li.attributes) %]"},
+                {"BPU":"[% helpers.get_li_attr('publisher', '', li.attributes) %]"},
+                {"BPD":"[% helpers.get_li_attr('pubdate',   '', li.attributes) %]"},
+                {"BPH":"[% helpers.get_li_attr('pagination','', li.attributes) %]"}
+            ],
+            [%- ftx_vals = []; 
+                FOR note IN li.lineitem_notes; 
+                    NEXT UNLESS note.vendor_public == 't'; 
+                    ftx_vals.push(note.value); 
+                END; 
+                IF xtra_ftx;           ftx_vals.unshift(xtra_ftx); END; 
+                IF ftx_vals.size == 0; ftx_vals.unshift('');       END;  # BT needs FTX+LIN for every LI, even if it is an empty one
+            -%]
+
+            "free-text":[ 
+                [% FOR note IN ftx_vals -%] "[% note %]"[% UNLESS loop.last %], [% END %][% END %] 
+            ],            
+            "quantity":[% li.lineitem_details.size %]
+        }[% UNLESS loop.last %],[% END %]
+        [%-# TODO: lineitem details (later) -%]
+        [% END %]
+        ],
+        "line_items":[% target.lineitems.size %]
+     }]  [%# close ORDERS array %]
+   }]    [%# close  body  array %]
+}
+[% END %]
+[% tempo = PROCESS big_block; helpers.escape_json(tempo) %]
+
+$$ 
+WHERE id=23;
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0434.data.merge_template_container_type.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,9 @@
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0434');
+
+INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('template_merge','Template Merge Container');
+
+COMMIT;
+

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0435.schema.template-add-field.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0435.schema.template-add-field.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0435.schema.template-add-field.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0435.schema.template-add-field.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,78 @@
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0435'); -- miker
+
+CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT ) RETURNS TEXT AS $_$
+
+    use MARC::Record;
+    use MARC::File::XML (BinaryEncoding => 'UTF-8');
+    use strict;
+
+    my $target_xml = shift;
+    my $source_xml = shift;
+    my $field_spec = shift;
+
+    my $target_r = MARC::Record->new_from_xml( $target_xml );
+    my $source_r = MARC::Record->new_from_xml( $source_xml );
+
+    return $target_xml unless ($target_r && $source_r);
+
+    my @field_list = split(',', $field_spec);
+
+    my %fields;
+    for my $f (@field_list) {
+        $f =~ s/^\s*//; $f =~ s/\s*$//;
+        if ($f =~ /^(.{3})(\w*)(?:\[([^]]*)\])?$/) {
+            my $field = $1;
+            $field =~ s/\s+//;
+            my $sf = $2;
+            $sf =~ s/\s+//;
+            my $match = $3;
+            $match =~ s/^\s*//; $match =~ s/\s*$//;
+            $fields{$field} = { sf => [ split('', $sf) ] };
+            if ($match) {
+                my ($msf,$mre) = split('~', $match);
+                if (length($msf) > 0 and length($mre) > 0) {
+                    $msf =~ s/^\s*//; $msf =~ s/\s*$//;
+                    $mre =~ s/^\s*//; $mre =~ s/\s*$//;
+                    $fields{$field}{match} = { sf => $msf, re => qr/$mre/ };
+                }
+            }
+        }
+    }
+
+    for my $f ( keys %fields) {
+        if ( @{$fields{$f}{sf}} ) {
+            for my $from_field ($source_r->field( $f )) {
+                my @tos = $target_r->field( $f );
+                if (!@tos) {
+                    my @new_fields = map { $_->clone } $source_r->field( $f );
+                    $target_r->insert_fields_ordered( @new_fields );
+                } else {
+                    for my $to_field (@tos) {
+                        if (exists($fields{$f}{match})) {
+                            next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
+                        }
+                        my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}{sf}};
+                        $to_field->add_subfields( @new_sf );
+                    }
+                }
+            }
+        } else {
+            my @new_fields = map { $_->clone } $source_r->field( $f );
+            $target_r->insert_fields_ordered( @new_fields );
+        }
+    }
+
+    $target_xml = $target_r->as_xml_record;
+    $target_xml =~ s/^<\?.+?\?>$//mo;
+    $target_xml =~ s/\n//sgo;
+    $target_xml =~ s/>\s+</></sgo;
+
+    return $target_xml;
+
+$_$ LANGUAGE PLPERLU;
+
+COMMIT;
+

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0436.schema.multiple-rules.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0436.schema.multiple-rules.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0436.schema.multiple-rules.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0436.schema.multiple-rules.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,50 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0436'); -- miker
+
+CREATE OR REPLACE FUNCTION vandelay.compile_profile ( incoming_xml TEXT ) RETURNS vandelay.compile_profile AS $_$
+DECLARE
+    output              vandelay.compile_profile%ROWTYPE;
+    profile             vandelay.merge_profile%ROWTYPE;
+    profile_tmpl        TEXT;
+    profile_tmpl_owner  TEXT;
+    add_rule            TEXT := '';
+    strip_rule          TEXT := '';
+    replace_rule        TEXT := '';
+    preserve_rule       TEXT := '';
+
+BEGIN
+
+    profile_tmpl := (oils_xpath('//*[@tag="905"]/*[@code="t"]/text()',incoming_xml))[1];
+    profile_tmpl_owner := (oils_xpath('//*[@tag="905"]/*[@code="o"]/text()',incoming_xml))[1];
+
+    IF profile_tmpl IS NOT NULL AND profile_tmpl <> '' AND profile_tmpl_owner IS NOT NULL AND profile_tmpl_owner <> '' THEN
+        SELECT  p.* INTO profile
+          FROM  vandelay.merge_profile p
+                JOIN actor.org_unit u ON (u.id = p.owner)
+          WHERE p.name = profile_tmpl
+                AND u.shortname = profile_tmpl_owner;
+
+        IF profile.id IS NOT NULL THEN
+            add_rule := COALESCE(profile.add_spec,'');
+            strip_rule := COALESCE(profile.strip_spec,'');
+            replace_rule := COALESCE(profile.replace_spec,'');
+            preserve_rule := COALESCE(profile.preserve_spec,'');
+        END IF;
+    END IF;
+
+    add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),','),'');
+    strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),','),'');
+    replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),','),'');
+    preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),','),'');
+
+    output.add_rule := BTRIM(add_rule,',');
+    output.replace_rule := BTRIM(replace_rule,',');
+    output.strip_rule := BTRIM(strip_rule,',');
+    output.preserve_rule := BTRIM(preserve_rule,',');
+
+    RETURN output;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0437.schema.bytea-index-label_sortkey.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0437.schema.bytea-index-label_sortkey.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0437.schema.bytea-index-label_sortkey.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0437.schema.bytea-index-label_sortkey.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,9 @@
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0437'); -- miker
+
+DROP INDEX asset.asset_call_number_label_sortkey;
+CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(cast(label_sortkey as bytea));
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0438.schema.bytea-index-label.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0438.schema.bytea-index-label.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0438.schema.bytea-index-label.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0438.schema.bytea-index-label.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,9 @@
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0438'); -- miker
+
+DROP INDEX asset.asset_call_number_upper_label_id_owning_lib_idx;
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (cast(upper(label) as bytea),id,owning_lib);
+
+COMMIT;

Copied: branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0439.schema.function-bytea-index-label.sql (from rev 18366, trunk/Open-ILS/src/sql/Pg/upgrade/0439.schema.function-bytea-index-label.sql)
===================================================================
--- branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0439.schema.function-bytea-index-label.sql	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/sql/Pg/upgrade/0439.schema.function-bytea-index-label.sql	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,17 @@
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0439'); -- miker
+
+CREATE OR REPLACE FUNCTION oils_text_as_bytea (TEXT) RETURNS BYTEA AS $_$
+    SELECT CAST(REGEXP_REPLACE($1, $$\\$$, $$\\\\$$, 'g') AS BYTEA);
+$_$ LANGUAGE SQL IMMUTABLE;
+
+
+DROP INDEX asset.asset_call_number_upper_label_id_owning_lib_idx;
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(label),id,owning_lib);
+
+DROP INDEX asset.asset_call_number_label_sortkey;
+CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(oils_text_as_bytea(label_sortkey));
+
+COMMIT;

Modified: branches/serials-integration/Open-ILS/src/support-scripts/action_trigger_runner.pl
===================================================================
--- branches/serials-integration/Open-ILS/src/support-scripts/action_trigger_runner.pl	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/support-scripts/action_trigger_runner.pl	2010-10-18 14:05:06 UTC (rev 18372)
@@ -37,6 +37,7 @@
 my $opt_hooks;
 my $opt_process_hooks = 0;
 my $opt_granularity   = undef;
+my $opt_gran_only     = undef;
 
 (-f $opt_custom_filter) or undef($opt_custom_filter);   # discard default if no file exists
 
@@ -46,6 +47,7 @@
     'run-pending'      => \$opt_run_pending,
     'hooks=s'          => \$opt_hooks,
     'granularity=s'    => \$opt_granularity,
+    'granularity-only' => \$opt_gran_only,
     'process-hooks'    => \$opt_process_hooks,
     'debug-stdout'     => \$opt_debug_stdout,
     'custom-filters=s' => \$opt_custom_filter,
@@ -56,6 +58,11 @@
 
 my $max_sleep = $opt_max_sleep;
 
+#XXX need to figure out why this is required...
+$opt_gran_only = $opt_granularity ? 1 : 0;
+
+$opt_lockfile .= '.' . $opt_granularity if ($opt_granularity && $opt_gran_only);
+
 # typical passive hook filters
 my $hook_handlers = {
 
@@ -109,6 +116,9 @@
     --granularity=<label>
         Run events with {label} granularity setting, or no granularity setting
 
+    --granularity-only
+        Used in combination with --granularity, prevents the running of events with no granularity setting
+
     --debug-stdout
         Print server responses to stdout (as JSON) for debugging
 
@@ -169,10 +179,22 @@
 
 sub run_pending {
     $opt_verbose and print "run_pending: " .
-        ($opt_run_pending ? ($opt_granularity || 'ALL granularity') : 'SKIPPING') . "\n";
+        ($opt_run_pending ?
+            ($opt_granularity ?
+                ( $opt_granularity . (
+                    $opt_gran_only ?
+                        ' ONLY' : 
+                        ' and NON-GRANULAR'
+                    )
+                ) :
+                'NON-GRANULAR'
+            ) :
+            'SKIPPING'
+        ) . "\n";
+
     return unless $opt_run_pending;
     my $ses = OpenSRF::AppSession->create('open-ils.trigger');
-    my $req = $ses->request('open-ils.trigger.event.run_all_pending' => $opt_granularity);
+    my $req = $ses->request('open-ils.trigger.event.run_all_pending' => $opt_granularity => $opt_gran_only);
 
     my $check_lockfile = 1;
     while (my $resp = $req->recv(timeout => $req_timeout)) {
@@ -192,7 +214,7 @@
 
 # check the lockfile
 if (-e $opt_lockfile) {
-    die "I'm already running with lockfile $opt_lockfile\n" if (!$opt_process_hooks);
+    die "I'm already running with lockfile $opt_lockfile\n" unless ($opt_process_hooks or $opt_granularity);
     # sleeping loop if we're in --process-hooks mode
     while ($max_sleep >= 0 && sleep(1)) {
         last unless ( -e $opt_lockfile ); 

Modified: branches/serials-integration/Open-ILS/src/support-scripts/edi_fetcher.pl
===================================================================
--- branches/serials-integration/Open-ILS/src/support-scripts/edi_fetcher.pl	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/support-scripts/edi_fetcher.pl	2010-10-18 14:05:06 UTC (rev 18372)
@@ -140,8 +140,6 @@
 
 __END__
 
-=pod
-
 =head1 NAME
 
 edi_fetcher.pl - A script for retrieving and processing EDI files from remote accounts.
@@ -159,8 +157,8 @@
 
 =head1 OPTIONS
 
-    --account=[id]  Target one account, whether or not it is inactive.
-    --inactive      Includes inactive provider accounts (default OFF, forced ON if --account specified)
+  --account=[id]  Target one account, whether or not it is inactive.
+  --inactive      Includes inactive provider accounts (default OFF, forced ON if --account specified)
 
 =head1 ARGUMENTS
 

Modified: branches/serials-integration/Open-ILS/src/support-scripts/edi_pusher.pl
===================================================================
--- branches/serials-integration/Open-ILS/src/support-scripts/edi_pusher.pl	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/support-scripts/edi_pusher.pl	2010-10-18 14:05:06 UTC (rev 18372)
@@ -32,6 +32,7 @@
 
 my %defaults = (
     'quiet' => 0,
+    'test'  => 0,   # TODO
     'max-batch-size=i' => -1
 );
 
@@ -48,8 +49,8 @@
 
 $opts->{verbose} = 0 if $opts->{quiet};
 
+print "FTP_PASSIVE is ", ($ENV{FTP_PASSIVE} ? "ON" : "OFF"),  "\n";
 
-# print Dumper($defs);
 print "\nHook '$hook' is used in ", scalar(@$defs), " event definition(s):\n";
 
 $Data::Dumper::Indent = 1;
@@ -99,7 +100,10 @@
     if ($opts->{verbose}) {
         # $subq->{'select'}->{'acqedim'} = ['id', 'purchase_order', 'message_type', 'status'];
         my $excluded = $e->json_query($subq);
-        print "Excluded: ", scalar(@$excluded), " purchase order(s):\n", Dumper(\@$excluded), "\n";
+        print "Excluded: ", scalar(@$excluded), " purchase order(s):\n";
+        my $z = 0;
+        print map {sprintf "%7d%s", $_, (++$z % 5) ? '' : "\n"} sort {$a <=> $b} map {$_->{purchase_order}} @$excluded;
+        print "\n";
     }
 
     my $events = $e->json_query($query);
@@ -155,6 +159,11 @@
 
         print "\ntarget->provider->edi_default->id: ", $target->provider->edi_default->id, "\n";
         my $logstr2 = sprintf "event %s, PO %s, template_output %s", $_->{id}, $message->purchase_order, $event->template_output->id;
+        if ($opts->{test}) {
+            print "Test mode, skipping translation/send\n";
+            next;
+        }
+
         printf "\nNow calling attempt_translation for $logstr2\n\n";
 
         unless (OpenILS::Application::Acq::EDI->attempt_translation($message, 1)) {
@@ -192,3 +201,41 @@
 }
 
 print "\ndone\n";
+
+__END__
+
+=head1 NAME
+
+edi_pusher.pl - A script for generating and sending EDI files to remote accounts.
+
+=head1 DESCRIPTION
+
+This script is expected to be run via crontab, for the purpose of retrieving vendor EDI files.
+
+=head1 OPTIONS
+
+  --max-batch-size=i  Limit the processing to a set number of events.
+
+=head1 TODO
+
+More docs here.
+
+=head1 USAGE
+
+B<FTP_PASSIVE=1> is recommended.  Depending on your vendors' and your own network environments, you may want to set/export
+the environmental variable FTP_PASSIVE like:
+
+    export FTP_PASSIVE=1
+    # or
+    FTP_PASSIVE=1 Open-ILS/src/support-scripts/edi_pusher.pl
+
+=head1 SEE ALSO
+
+    OpenILS::Utils::Cronscript
+    edi_fetcher.pl
+
+=head1 AUTHOR
+
+Joe Atzberger <jatzberger at esilibrary.com>
+
+=cut

Modified: branches/serials-integration/Open-ILS/src/support-scripts/marc_export
===================================================================
--- branches/serials-integration/Open-ILS/src/support-scripts/marc_export	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/src/support-scripts/marc_export	2010-10-18 14:05:06 UTC (rev 18372)
@@ -10,6 +10,7 @@
 use OpenSRF::Utils::SettingsClient;
 use OpenILS::Application::AppUtils;
 use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::CStoreEditor;
 
 use MARC::Record;
 use MARC::File::XML;
@@ -21,11 +22,12 @@
 
 my @formats = qw/USMARC UNIMARC XML BRE/;
 
-my ($config,$format,$encoding,$location,$dollarsign,$idl,$help,$holdings,$timeout) = ('/openils/conf/opensrf_core.xml','USMARC','MARC8','','$',0,undef,undef,0);
+my ($config,$format,$encoding,$location,$dollarsign,$idl,$help,$holdings,$timeout,$export_mfhd) = ('/openils/conf/opensrf_core.xml','USMARC','MARC8','','$',0,undef,undef,0,undef);
 
 GetOptions(
         'help'       => \$help,
         'items'      => \$holdings,
+        'mfhd'       => \$export_mfhd,
         'location=s' => \$location,
         'money=s'    => \$dollarsign,
         'config=s'   => \$config,
@@ -40,9 +42,11 @@
 Usage: $0 [options]
  --help or -h       This screen.
  --config or -c     Configuration file [/openils/conf/opensrf_core.xml]
- --format or -f     Output format (USMARC, UNIMARC, XML) [USMARC]
+ --format or -f     Output format (USMARC, UNIMARC, XML, BRE) [USMARC]
  --encoding or -e   Output Encoding (UTF-8, ISO-8859-?, MARC8) [MARC8]
  --items or -i      Include items (holdings) in the output
+ --mfhd             Export serial MFHD records for associated bib records
+                    Not compatible with --format=BRE
  --xml-idl or -x    Location of the IDL XML
  --location or -l   MARC Location Code for holdings from
                     http://www.loc.gov/marc/organizations/orgshome.html
@@ -91,6 +95,8 @@
 Fieldmapper->import(IDL => $idl);
 
 my $ses = OpenSRF::AppSession->create('open-ils.cstore');
+OpenILS::Utils::CStoreEditor::init();
+my $editor = OpenILS::Utils::CStoreEditor->new();
 
 print <<HEADER if ($format eq 'XML');
 <?xml version="1.0" encoding="$encoding"?>
@@ -222,6 +228,29 @@
         import MARC::File::XML; # reset SAX parser so that one bad record doesn't kill the entire export
     };
 
+    if ($export_mfhd) {
+        my $mfhds = $editor->search_serial_record_entry({record => $i, deleted => 'f'});
+        foreach my $mfhd (@$mfhds) {
+            try {
+                my $r = MARC::Record->new_from_xml( $mfhd->marc, $encoding, $format );
+
+                if (uc($format) eq 'XML') {
+                    my $xml = $r->as_xml_record;
+                    $xml =~ s/^<\?.+?\?>$//mo;
+                    print $xml;
+                } elsif (uc($format) eq 'UNIMARC') {
+                    print $r->as_usmarc;
+                } elsif (uc($format) eq 'USMARC') {
+                    print $r->as_usmarc;
+                }
+            } otherwise {
+                my $e = shift;
+                warn "\n$e\n";
+                import MARC::File::XML; # reset SAX parser so that one bad record doesn't kill the entire export
+            };
+        }
+    }
+
     stats() if (! ($count{bib} % 50 ));
 }
 

Copied: branches/serials-integration/Open-ILS/src/templates/password-reset/strings.fr-CA (from rev 18366, trunk/Open-ILS/src/templates/password-reset/strings.fr-CA)
===================================================================
--- branches/serials-integration/Open-ILS/src/templates/password-reset/strings.fr-CA	                        (rev 0)
+++ branches/serials-integration/Open-ILS/src/templates/password-reset/strings.fr-CA	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,15 @@
+BUTTON_SUBMIT=Envoyer
+REQUEST_TITLE=Formulaire de demande de réinitialisation du mot de passe du réseau de la Bibliothèque
+IDENTIFY_YOURSELF=Entrez votre nom d’utilisateur ou votre code à barres pour indiquer votre compte de bibliothèque et demander la réinitialisation de votre mot de passe.
+REQUEST_SUCCESS=Votre nom d’utilisateur ou votre code à barres a été présenté pour la réinitialisation de votre mot de passe. S’il existe un compte correspondant assorti d’une adresse électronique, vous recevrez bientôt à cette adresse un message contenant les instructions pour réinitialiser votre mot de passe.
+BARCODE_PROMPT=Code à barres :
+USERNAME_PROMPT=Nom d’utilisateur :
+EMAIL_PROMPT=Adresse électronique liée au compte :
+NO_SESSION=Impossible de trouver la session de réinitialisation du mot de passe demandée.
+NO_MATCH=Les mots de passe ne concordent pas. Veuillez réessayer.
+NOT_ACTIVE=Cette demande de réinitialisation du mot de passe est inactive. Votre mot de passe n’a pas été réinitialisé.
+NOT_STRONG=Le mot de passe que vous avez choisi n’est pas assez complexe pour protéger votre compte. Votre mot de passe n’a pas été réinitialisé.
+SUCCESS=Mot de passe réinitialisé.
+TITLE=Réinitialiser le mot de passe du réseau de la Bibliothèque
+PASSWORD_PROMPT=Nouveau mot de passe :
+PASSWORD_PROMPT2=Entrez de nouveau le mot de passe :

Modified: branches/serials-integration/Open-ILS/web/conify/global/permission/grp_tree.html
===================================================================
--- branches/serials-integration/Open-ILS/web/conify/global/permission/grp_tree.html	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/conify/global/permission/grp_tree.html	2010-10-18 14:05:06 UTC (rev 18372)
@@ -196,6 +196,7 @@
 						}
 
 						editor_pane_application_perm.setValue( this.store.getValue( current_group, 'application_perm' ) );
+						editor_pane_hold_priority.setValue( this.store.getValue( current_group, 'hold_priority' ) );
 
 						editor_pane_usergroup.setChecked( this.store.getValue( current_group, 'usergroup' ) == 't' ? true : false );
 ]]>
@@ -288,6 +289,24 @@
 								</td>
 							</tr>
 							<tr>
+								<th>&conify.grp_tree.hold_priority.label;</th>
+								<td>
+									<div
+									  id="editor_pane_hold_priority"
+									  dojoType="dijit.form.NumberSpinner"
+									  jsId="editor_pane_hold_priority"
+									>
+										<script type="dojo/connect" event="onChange">
+<![CDATA[
+											if (current_group && this.getValue()) {
+												group_store.setValue( current_group, "hold_priority", this.getValue() );
+											}
+]]>
+										</script>
+									</div>
+								</td>
+							</tr>
+							<tr>
 								<th>&conify.grp_tree.parent_group.label;</th>
 								<td>
 									<div

Modified: branches/serials-integration/Open-ILS/web/js/dojo/MARC/Field.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/MARC/Field.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/MARC/Field.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -40,7 +40,7 @@
 
         subfield : function (code) {
             var list = dojo.filter( this.subfields, function (s) {
-                if (s[0] == code) return true; return true;
+                if (s[0] == code) return true; return false;
             });
             if (list.length == 1) return list[0];
             return list;

Modified: branches/serials-integration/Open-ILS/web/js/dojo/MARC/Record.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/MARC/Record.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/MARC/Record.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -27,7 +27,7 @@
 
         constructor : function(kwargs) {
             this.fields = [];
-            this.leader = '';
+            this.leader = '00000cam a2200205Ka 4500';
 
             if (kwargs.delimiter) this.delimiter = kwargs.delimiter;
             if (kwargs.onLoad) this.onLoad = kwargs.onLoad;
@@ -57,7 +57,11 @@
             return list;
         },
 
-        subfield : function (spec, code) { return this.field(spec)[0].subfield(code) },
+        subfield : function (spec, code) {
+            var f = this.field(spec);
+            if (dojo.isArray(f)) f = f[0];
+            return f.subfield(code)
+        },
 
         appendFields : function () {
             var me = this;
@@ -151,7 +155,7 @@
 
         fromXmlDocument : function (mxml) {
             var me = this;
-            me.leader = dojox.xml.parser.textContent(dojo.query('leader', mxml)[0]) || '';
+            me.leader = dojox.xml.parser.textContent(dojo.query('leader', mxml)[0]) || '00000cam a2200205Ka 4500';
 
             dojo.forEach( dojo.query('controlfield', mxml), function (cf) {
                 me.fields.push(
@@ -240,7 +244,7 @@
                     // skip comment lines
                 } else if (isControlField(current_line)) {
                     if (line_tag(current_line) == 'LDR') {
-                        me.leader = cf_line_data(current_line) || '';
+                        me.leader = cf_line_data(current_line) || '00000cam a2200205Ka 4500';
                     } else {
                         me.fields.push(
                             new MARC.Field({

Modified: branches/serials-integration/Open-ILS/web/js/dojo/openils/BibTemplate.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/openils/BibTemplate.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/openils/BibTemplate.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -36,6 +36,9 @@
             this.locale = kwargs.locale || OpenSRF.locale || 'en-US';
             this.nodelay = kwargs.delay == false;
 
+            if (this.xml && this.xml instanceof String)
+                this.xml = dojox.xml.parser.parse(this.xml);
+
             this.mode = 'biblio-record_entry';
             this.default_datatype = 'marcxml-uris';
             if (kwargs.metarecord) {
@@ -56,17 +59,16 @@
         },
 
         textContent : function (node) {
-            var content = '';
             if (node) {
-                if(window.ActiveXObject) content = node.text;
-                else content = node.textContent;
+                if (node instanceof HTMLElement) return node.innerText || node.textContent;
+                return dojox.xml.parser.textContent(node);
             }
-            return content;
+            return '';
         },
 
         render : function() {
 
-            var all_slots = dojo.query('*[type^=opac/slot-data]', this.root);
+            var all_slots = dojo.query('*[type^="opac/slot-data"]', this.root);
             var default_datatype = this.default_datatype;
         
             var slots = {};
@@ -99,10 +101,10 @@
                                 var item_limit = parseInt(slot.getAttribute('limit'));
                                 var item_offset = parseInt(slot.getAttribute('offset')) || 0;
 
-                                var pre_render_callbacks = dojo.query( '*[type=opac/call-back+pre-render]', slot );
-                                var post_render_callbacks = dojo.query( '*[type=opac/call-back+post-render]', slot );
-                                var pre_query_callbacks = dojo.query( '*[type=opac/call-back+pre-query]', slot );
-                                var post_query_callbacks = dojo.query( '*[type=opac/call-back+post-query]', slot );
+                                var pre_render_callbacks = dojo.query( '*[type="opac/call-back+pre-render"]', slot );
+                                var post_render_callbacks = dojo.query( '*[type="opac/call-back+post-render"]', slot );
+                                var pre_query_callbacks = dojo.query( '*[type="opac/call-back+pre-query"]', slot );
+                                var post_query_callbacks = dojo.query( '*[type="opac/call-back+post-query"]', slot );
 
                                 // Do pre-query stuff
                                 dojo.forEach(pre_query_callbacks, function (cb) {
@@ -119,7 +121,7 @@
                                     if (item_list.length) item_list = BT.subsetNL(item_list, item_offset, item_offset + item_limit);
                                 }
 
-                                // Do post-query stuff, only if there's an item list!
+                                // Do post-query stuff
                                 dojo.forEach(post_query_callbacks, function (cb) {
                                     try { (new Function( 'item_list', 'BT', 'slotXML', 'slot', unescape(cb.innerHTML) ))(item_list,BT,bib,slot) } catch (e) {/*meh*/}
                                 });
@@ -139,7 +141,7 @@
                                     var template_value_count = 0;
 
                                     dojo.query(
-                                        '*[type=opac/template-value]',
+                                        '*[type="opac/template-value"]',
                                         slot
                                     ).orphan().forEach(function(x) {
                                         var name = x.getAttribute('name');
@@ -154,7 +156,7 @@
                                     if (template_value_count > 0) slot.innerHTML = dojo.string.substitute( unescape(slot.innerHTML), template_values );
                                 }
 
-                                var handler_node = dojo.query( '*[type=opac/slot-format]', slot )[0];
+                                var handler_node = dojo.query( '*[type="opac/slot-format"]', slot )[0];
                                 if (handler_node) slot_handler = new Function('item_list', 'BT', 'slotXML', 'slot', 'item', dojox.xml.parser.textContent(handler_node) || handler_node.innerHTML);
                                 else slot_handler = new Function('item_list', 'BT', 'slotXML', 'slot', 'item','return dojox.xml.parser.textContent(item) || item.innerHTML;');
 

Modified: branches/serials-integration/Open-ILS/web/js/dojo/openils/PermaCrud.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/openils/PermaCrud.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/openils/PermaCrud.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -26,11 +26,13 @@
         session : null,
         authtoken : null,
         connnected : false,
+        authoritative : false,
 
         constructor : function ( kwargs ) {
             kwargs = kwargs || {};
 
             this.authtoken = kwargs.authtoken;
+            this.authoritative = kwargs.authoritative;
 
             this.session =
                 kwargs.session ||
@@ -66,8 +68,42 @@
                 return false;
             }
         },
-        
 
+        _session_request : function ( args /* hash */, commitOnComplete /* set to true, else no */ ) {
+
+            var me = this;
+            var endstyle = 'rollback';
+            var aopts = dojo.mixin({}, args);
+            args = aopts;
+            if (commitOnComplete) endstyle = 'commit';
+
+            if (me.authoritative) {
+                if (!me.connected) me.connect();
+                if (args.timeout && !args.oncomplete && !args.onresponse) { // pure sync call
+                    args.oncomplete = function (r) {
+                        me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
+                        me.session.disconnect();
+                        me.disconnect();
+                    };
+                } else if (args.oncomplete) { // there's an oncomplete, fire that, and then end the transaction
+                    var orig_oncomplete = args.oncomplete;
+                    args.oncomplete = function (r) {
+                        var ret;
+                        try {
+                            ret = orig_oncomplete(r);
+                        } finally {
+                            me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
+                            me.session.disconnect();
+                            me.disconnect();
+                        }
+                        return ret;
+                    };
+                }
+                me.session.request('open-ils.pcrud.transaction.begin', me.auth());
+            }
+            return me.session.request( args );
+        },
+
         retrieve : function ( fm_class /* Fieldmapper class hint */, id /* Fieldmapper object primary key value */,  opts /* Option hash */) {
             if(!opts) opts = {};
             var req_hash = dojo.mixin(
@@ -80,7 +116,7 @@
             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
 
             var _pcrud = this;
-            var req = this.session.request( req_hash );
+            var req = this._session_request( req_hash );
 
             if (!req.onerror)
                 req.onerror = function (r) { throw js2JSON(r); };
@@ -129,7 +165,7 @@
             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
 
             var _pcrud = this;
-            var req = this.session.request( req_hash );
+            var req = this._session_request( req_hash );
 
             if (!req.onerror)
                 req.onerror = function (r) { throw js2JSON(r); };
@@ -175,7 +211,7 @@
             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
 
             var _pcrud = this;
-            var req = this.session.request( req_hash );
+            var req = this._session_request( req_hash );
 
             if (!req.onerror)
                 req.onerror = function (r) { throw js2JSON(r); };

Modified: branches/serials-integration/Open-ILS/web/js/dojo/openils/XUL.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/openils/XUL.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/openils/XUL.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -16,15 +16,17 @@
         if(openils.XUL.isXUL()) {
             try {
                 if(openils.XUL.enableXPConnect()) {
-			        var CacheClass = new Components.Constructor("@mozilla.org/openils_data_cache;1", "nsIOpenILS");
-			        return new CacheClass().wrappedJSObject.OpenILS.prototype.data;
+                    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 { 'error' : e };
             }
         }
 
-        return {};
+        return { 'error' : 'openils.XUL.isXUL() == false' };
     }
 
     openils.XUL.newTab = function(path, tabInfo, options) {

Modified: branches/serials-integration/Open-ILS/web/js/dojo/openils/actor/nls/register.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/openils/actor/nls/register.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/openils/actor/nls/register.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1,4 +1,5 @@
 {
+    "DEFAULT_ADDRESS_TYPE" : "MAILING",
     "DELETE_ADDRESS" : "Delete address ${0}?",
     "NEED_ADDRESS" : "An address is required during registration.",
     "DUPE_PATRON_NAME" : "Found ${0} patron(s) with the same name",

Modified: branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/FacetSidebar.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -14,6 +14,40 @@
  * ---------------------------------------------------------------------------
  */
 
+/*  Example markup:
+
+<div id='facetSidebarContainer' class='hide_me'>
+
+    <div class="side_bar_item" style="margin-top: 10px; font-weight: bold;">
+        <span>&navigate.facetRefine;</span>
+    </div>
+
+    <div
+        dojoType='openils.widget.FacetSidebar'
+        searchBox='facet_box'
+        searchSubmit='search_submit'
+        facetLimit='5'
+        maxValuesPerFacet='10'
+        classOrder='[{"name":"author","facetOrder":["personal","corporate"]},{"name":"subject","facetOrder":["topic"]},"series",{"name":"subject","facetOrder":["name","geographic"]}]'
+    >
+        <script type='dojo/method' event='populate'><![CDATA[
+            var f_sidebar = this;
+            attachEvt("result", "allRecordsReceived", function () {
+                if(!resultFacetKey) return;
+                if (f_sidebar.facetCacheKey) return; // already rendered it
+
+                dojo.removeClass('facetSidebarContainer','hide_me');
+
+                f_sidebar.facetCacheKey = resultFacetKey;
+                f_sidebar.render();
+            });
+        ]]></script>
+    </div>
+</div>
+
+ */
+
+
 if(!dojo._hasResource["openils.widget.FacetSidebar"]) {
 
     dojo._hasResource["openils.widget.FacetSidebar"] = true;
@@ -31,7 +65,8 @@
             facetData : {},
             facetCacheKey : '',
             searchBox : '',
-            classOrder : null,
+            classOrder : null, // Array of cmc.name values, OR array of objects with name and facetOrder properties
+            displayItemLimit : 999, // Number of distinctly described entries (classes or facets), that have values, to display from classOrder
             searchSubmit : '',
             facetLimit : 10,
             maxValuesPerFacet : 100,
@@ -75,21 +110,49 @@
                     classes = [];
                     dojo.forEach(
                         this.classOrder,
-                        function(x) { classes.push({name:x}); }
+                        function(x) {
+                            if (dojo.isObject(x)) classes.push(x);
+                            else classes.push({name:x});
+                        }
                     );
                 }
 
+                var displayedItems = 0;
                 var me = this;
                 dojo.forEach(
                     classes,
                     function (x) {
-                        var possible_facets = dojo.filter(
-                            openils.widget.Searcher._cache.arr.cmf,
-                            function (y) {
-                                if (y.field_class == x.name && facetData[y.id]) return 1;
-                                return 0;
-                            }
-                        );
+                        var possible_facets = [];
+                        if (x.facetOrder) {
+                            dojo.forEach(x.facetOrder, function(fname) {
+                                var maybe_facet = dojo.filter(
+                                    openils.widget.Searcher._cache.arr.cmf,
+                                    function (y) {
+                                        if (y.field_class == x.name && y.name == fname && facetData[y.id]) {
+                                            if (displayedItems < me.displayItemLimit) {
+                                                displayedItems++;
+                                                return 1;
+                                            }
+                                        }
+                                        return 0;
+                                    }
+                                )[0];
+                                if (maybe_facet) possible_facets.push(maybe_facet);
+                            });
+                        } else {
+                            possible_facets = dojo.filter(
+                                openils.widget.Searcher._cache.arr.cmf,
+                                function (y) {
+                                    if (y.field_class == x.name && facetData[y.id]) {
+                                        if (displayedItems < me.displayItemLimit) {
+                                            displayedItems++;
+                                            return 1;
+                                        }
+                                    }
+                                    return 0;
+                                }
+                            );
+                        }
                         if (possible_facets.length > 0) me.addClass( x.name, possible_facets );
                     }
                 );

Modified: branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/dojo/openils/widget/ProgressDialog.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -48,7 +48,7 @@
                 }
                     
                 this.inherited(arguments);
-            },
+            }
         }
     );
 }

Modified: branches/serials-integration/Open-ILS/web/js/ui/base.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/ui/base.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/ui/base.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,6 +19,11 @@
     openils.User.authtoken = null;
     openils.User.workstation = null;
 
+    if(!authtoken && openils.XUL.isXUL()) {
+		stash = openils.XUL.getStash();
+		authtoken = stash.session.key
+	}
+
     if(authtoken) {
         user = new openils.User();
         delete user.sessionCache[authtoken];
@@ -38,6 +43,7 @@
             dojo.addOnLoad(function(){
                 if(openils.XUL.isXUL()) {
                     // let XUL handle the login dialog
+                    dump('getNewSession in base.js\n');
                     openils.XUL.getNewSession( function() { location.href = location.href } );
                 } else {
                     // in web-only mode, use the dojo login dialog

Modified: branches/serials-integration/Open-ILS/web/js/ui/default/actor/user/register.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/ui/default/actor/user/register.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/ui/default/actor/user/register.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -384,14 +384,14 @@
 
     /* fetch any values set for this user */
     userSettings = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve'],
+        ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve.authoritative'],
         {params : [openils.User.authtoken, userId, names]});
 }
 
 
 function uEditLoadUser(userId) {
     var patron = fieldmapper.standardRequest(
-        ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve'],
+        ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve.authoritative'],
         {params : [openils.User.authtoken, userId]}
     );
     openils.Event.parse_and_raise(patron);
@@ -1213,6 +1213,10 @@
             if(row.getAttribute('fmclass')) {
                 var widget = fleshFMRow(row, 'aua', {addr:id});
 
+                // make new addresses a default address type
+                if(id < 0 && row.getAttribute('fmfield') == 'address_type') 
+                    widget.widget.attr('value', localeStrings.DEFAULT_ADDRESS_TYPE); 
+
                 // make new addresses valid by default
                 if(id < 0 && row.getAttribute('fmfield') == 'valid') 
                     widget.widget.attr('value', true); 

Modified: branches/serials-integration/Open-ILS/web/js/ui/default/cat/authority/list.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/ui/default/cat/authority/list.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/ui/default/cat/authority/list.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -176,10 +176,21 @@
         dijit.byId('authTerm').attr('value', term);
         displayRecords();
     }
-    dojo.connect(dijit.byId('authTerm'), 'onBlur', function() {
-        dijit.byId('authPage').attr('value', 0);
-        displayRecords();
+
+    dojo.connect(dijit.byId('authAxis'), 'onKeyPress', function(evt) {
+        if (evt.keyCode == dojo.keys.ENTER) {
+            dijit.byId('authPage').attr('value', 0);
+            displayRecords();
+        }
+    }); 
+
+    dojo.connect(dijit.byId('authPage'), 'onKeyPress', function(evt) {
+        if (evt.keyCode == dojo.keys.ENTER) {
+            dijit.byId('authPage').attr('value', 0);
+            displayRecords();
+        }
     });
+
     dojo.connect(dijit.byId('authTerm'), 'onKeyPress', function(evt) {
         if (evt.keyCode == dojo.keys.ENTER) {
             dijit.byId('authPage').attr('value', 0);
@@ -187,6 +198,8 @@
         }
     });
 
+    dijit.byId('authTerm').focus();
+
 }
 dojo.addOnLoad(authListInit);
 
@@ -221,6 +234,7 @@
         + '/1' // replace with preceding line if OUs gain some meaning
         + '/' + dijit.byId('authTerm').attr('value')
         + '/' + dijit.byId('authPage').attr('value')
+        + '/' + '20' // 20 results per page
     ;
     dojo.xhrGet({"url":url, "handleAs":"xml", "content":{"format":"marcxml"}, "preventCache": true, "load":displayAuthorities });
 }

Modified: branches/serials-integration/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1447,6 +1447,7 @@
  * Logout the patron and return to the login page
  */
 SelfCheckManager.prototype.logoutPatron = function(print) {
+    progressDialog.show(true); // prevent patron from clicking logout link twice
     if(print && this.checkouts.length) {
         this.printSessionReceipt(
             function() {

Modified: branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/action_trigger/event_definition.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/action_trigger/event_definition.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/action_trigger/event_definition.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -16,7 +16,8 @@
 function loadEventDef() { 
     eventDefGranularity.attr('value', null);
     edGrid.overrideEditWidgets.granularity = eventDefGranularity;
-    edGrid.loadAll({order_by:{atevdef : 'hook'}}); 
+    edGrid.overrideEditWidgets.granularity.shove = {"create": ""};
+    edGrid.loadAll({order_by:{atevdef : 'name'}}); 
     openils.widget.Textarea.width = '600px';
     openils.widget.Textarea.height = '600px';
     edGrid.overrideEditWidgetClass.template = 'openils.widget.Textarea';

Modified: branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/asset/copy_location_order.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/asset/copy_location_order.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/ui/default/conify/global/asset/copy_location_order.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -34,12 +34,14 @@
 function filterGrid(org) {
 
     // fetch the locations and order entries
-    var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-    orders = pcrud.search('acplo', {org : org}, {order_by : {acplo : 'position'}});
-    locations = pcrud.search('acpl', 
-        {owning_lib : fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true)}, 
-        {order_by : {acpl : 'name'}}
-    ); 
+    if(!orders) {
+        var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
+        orders = pcrud.search('acplo', {org : org}, {order_by : {acplo : 'position'}});
+        locations = pcrud.search('acpl', 
+            {owning_lib : fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true)}, 
+            {order_by : {acpl : 'name'}}
+        ); 
+    }
 
     // init the DnD environment
     source.selectAll();
@@ -80,30 +82,10 @@
 }
 
 function applyChanges() {
-    progressDialog.show(true);
-    if(orders.length) 
-        deleteOrders(createOrders);
-    else
-        createOrders();
-}
+    progressDialog.show();
 
-function deleteOrders(onload) {
-    // delete the existing order entries in preparation for new ones
-    var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-    pcrud.eliminate(
-        orders,
-        {
-            async : true,
-            oncomplete : function() {
-                if(onload) onload();
-            }
-        }
-    );
-}
-
-function createOrders() {
-
     var newOrders = [];
+    var contextOrg = contextOrgSelector.attr('value');
 
     // pull the locations out of the DnD environment and create order entries for them
     dojo.forEach(
@@ -113,21 +95,27 @@
             var o = new fieldmapper.acplo();
             o.position(newOrders.length + 1);
             o.location(item.type[0]); // location.id() is stored in DnD item type
-            o.org(contextOrgSelector.attr('value'));
+            o.org(contextOrg);
             newOrders.push(o);
         }
     );
 
-    // send the order entries off to the server
-    var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
-    pcrud.create(
-        newOrders,
+    fieldmapper.standardRequest(
+        ['open-ils.circ', 'open-ils.circ.copy_location_order.update'],
         {
             async : true,
-            oncomplete : function(r) {
-                progressDialog.hide();
-                filterGrid(contextOrgSelector.attr('value'));
-            }
+            params : [openils.User.authtoken, newOrders],
+            onresponse : function(r) {
+                if(r = openils.Util.readResponse(r)) {
+                    if(r.orders) {
+                        orders = r.order;
+                        progressDialog.hide();
+                        filterGrid(contextOrg);
+                        return;
+                    } 
+                    progressDialog.update(r);
+                }
+            },
         }
     );
 }

Modified: branches/serials-integration/Open-ILS/web/js/ui/default/vandelay/vandelay.js
===================================================================
--- branches/serials-integration/Open-ILS/web/js/ui/default/vandelay/vandelay.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/js/ui/default/vandelay/vandelay.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1339,7 +1339,7 @@
     };
 
     new openils.User().buildPermOrgSelector(
-        '"ADMIN_MERGE_PROFILE', profileContextOrgSelector, null, connect);
+        'ADMIN_MERGE_PROFILE', profileContextOrgSelector, null, connect);
 }
 
 function buildProfileGrid() {

Modified: branches/serials-integration/Open-ILS/web/opac/common/js/opac_utils.js
===================================================================
--- branches/serials-integration/Open-ILS/web/opac/common/js/opac_utils.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/opac/common/js/opac_utils.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -536,6 +536,7 @@
 	if ses != G.user.session, we also force a grab */
 function grabUser(ses, force) {
 
+    _debug("grabUser auth token = " + ses);
 	if(!ses && isXUL()) {
 		stash = fetchXULStash();
 		ses = stash.session.key
@@ -545,6 +546,7 @@
 	if(!ses) {
 		ses = cookieManager.read(COOKIE_SES);
 		/* https cookies don't show up in http servers.. */
+		_debug("cookie auth token = " + ses);
 	}
 
 	if(!ses) return false;
@@ -563,6 +565,7 @@
 
         if(isXUL()) {
             dojo.require('openils.XUL');
+            dump('getNewSession in opac_utils.js\n');
             openils.XUL.getNewSession( 
                 function(success, authtoken) { 
                     if(success) {

Copied: branches/serials-integration/Open-ILS/web/opac/extras/circ (from rev 18366, trunk/Open-ILS/web/opac/extras/circ)

Modified: branches/serials-integration/Open-ILS/web/opac/locale/en-US/conify.dtd
===================================================================
--- branches/serials-integration/Open-ILS/web/opac/locale/en-US/conify.dtd	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/opac/locale/en-US/conify.dtd	2010-10-18 14:05:06 UTC (rev 18372)
@@ -113,6 +113,7 @@
  <!ENTITY conify.grp_tree.group_name.label "Group Name">
  <!ENTITY conify.grp_tree.description.label "Description">
  <!ENTITY conify.grp_tree.permission_interval.label "Permission Interval">
+ <!ENTITY conify.grp_tree.hold_priority.label "Hold Priority">
  <!ENTITY conify.grp_tree.editing_permission.label "Editing Permission">
  <!ENTITY conify.grp_tree.parent_group.label "Parent Group">
  <!ENTITY conify.grp_tree.user_group.label "User Group">

Modified: branches/serials-integration/Open-ILS/web/opac/locale/en-US/lang.dtd
===================================================================
--- branches/serials-integration/Open-ILS/web/opac/locale/en-US/lang.dtd	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/opac/locale/en-US/lang.dtd	2010-10-18 14:05:06 UTC (rev 18372)
@@ -330,6 +330,13 @@
 <!ENTITY staff.cat.popup.edit.record.window.key "">
 <!ENTITY staff.cat.popup.edit_record.tab "Edit Record (Tab)">
 <!ENTITY staff.cat.popup.edit_record.window "Edit Record (Window)">
+<!ENTITY staff.cat.record_buckets.merge_records.merge_lead "Merge these records? (Select the 'lead' record first)">
+<!ENTITY staff.cat.record_buckets.merge_records.button.label "Merge">
+<!ENTITY staff.cat.record_buckets.merge_records.button.accesskey "M">
+<!ENTITY staff.cat.record_buckets.merge_records.cancel_button.label "Cancel">
+<!ENTITY staff.cat.record_buckets.merge_records.cancel_button.accesskey "C">
+<!ENTITY staff.cat.record_buckets.merge_records.lead "Lead Record?">
+<!ENTITY staff.cat.record_buckets.merge_records.remove_from_consideration "Remove from consideration?">
 <!ENTITY staff.cat.search_advanced "Advanced">
 <!ENTITY staff.cat.search_advanced.key "V">
 <!ENTITY staff.cat.search_all "Keyword">
@@ -835,6 +842,8 @@
 <!ENTITY staff.main.menu.cat.edit_user_buckets.label "Manage User Buckets">
 <!ENTITY staff.main.menu.cat.key "a">
 <!ENTITY staff.main.menu.cat.label "Cataloging">
+<!ENTITY staff.main.menu.cat.marc_batch_edit.label "MARC Batch Edit">
+<!ENTITY staff.main.menu.cat.marc_batch_edit.accesskey "E">
 <!ENTITY staff.main.menu.cat.retrieve_last_record.accesskey "L">
 <!ENTITY staff.main.menu.cat.retrieve_last_record.label "Retrieve Last Record">
 <!ENTITY staff.main.menu.cat.search_tcn.accesskey "T">
@@ -1904,6 +1913,7 @@
 <!ENTITY staff.server.admin.index.library_settings "Library Settings Editor">
 <!ENTITY staff.server.admin.index.non_cataloged_types "Non-cataloged Types Editor">
 <!ENTITY staff.server.admin.index.statistical_categories "Statistical Categories Editor">
+<!ENTITY staff.server.admin.index.expired_holds_shelf "Expired Holds Shelf Printable Listing">
 <!ENTITY staff.server.admin.index.hold_pull_list "Pull List for Hold Requests">
 <!ENTITY staff.server.admin.index.testing "(Testing)">
 <!ENTITY staff.server.admin.index.hold_pull_list_classic "Pull List for Hold Requests (Classic)">
@@ -1915,14 +1925,6 @@
 <!ENTITY staff.server.admin.index.external_text_editor.label "External Text Editor Command">
 <!ENTITY staff.server.admin.index.external_text_editor.accesskey "x">
 
-<!ENTITY staff.server.admin.index.booking "Booking">
-<!ENTITY staff.server.admin.index.booking.reservation "Create/Cancel Reservations">
-<!ENTITY staff.server.admin.index.booking.pull_list "Pull List">
-<!ENTITY staff.server.admin.index.booking.capture "Capture">
-<!ENTITY staff.server.admin.index.booking.pickup "Pickup Reservations">
-<!ENTITY staff.server.admin.index.booking.return "Return Reservations">
-
-
 <!ENTITY staff.server.admin.org_settings.title "Evergreen: Library Settings Editor">
 <!-- This will be followed by the user's name -->
 <!ENTITY staff.server.admin.org_settings.greeting "Welcome ">
@@ -2580,7 +2582,8 @@
 <!ENTITY staff.cat.marcedit.validate.accesskey "V">
 <!ENTITY staff.cat.marcedit.save-button.accesskey "d">
 <!ENTITY staff.cat.marcedit.help.label "Help">
-<!ENTITY staff.cat.marcedit.swapEditor.label "Swap Editor Type">
+<!ENTITY staff.cat.marcedit.flatTextEditor.label "Flat-Text Editor">
+<!ENTITY staff.cat.marcedit.flatTextEditor.accesskey "">
 <!ENTITY staff.cat.marcedit.help.accesskey "H">
 <!ENTITY staff.cat.marcedit.caption.label "MARC Record">
 <!ENTITY staff.cat.marcedit.toggleFFE.label "Fixed Fields -- Record type: ">
@@ -2654,7 +2657,9 @@
 <!ENTITY staff.cat.record_buckets_overlay.box.label "Batch:">
 <!ENTITY staff.cat.record_buckets_overlay.sel_opac.label "Show All in Catalog">
 <!ENTITY staff.cat.record_buckets_overlay.transfer_title_holds.label "Transfer Title Holds">
-<!ENTITY staff.cat.record_buckets_overlay.transfer_title_holds.accesskey "Transfer Title Holds">
+<!ENTITY staff.cat.record_buckets_overlay.transfer_title_holds.accesskey "T">
+<!ENTITY staff.cat.record_buckets_overlay.marc_batch_edit.label "MARC Batch Edit">
+<!ENTITY staff.cat.record_buckets_overlay.marc_batch_edit.accesskey "">
 <!ENTITY staff.cat.record_buckets_overlay.del_records.label "Delete All Records">
 <!ENTITY staff.cat.record_buckets_overlay.merge_records.label "Merge All Records">
 <!ENTITY staff.cat.record_buckets_overlay.export_records.label "Export All Records">
@@ -2751,9 +2756,11 @@
 <!ENTITY staff.cat.z3950.menuitem.save_columns.label "Save List Configuration">
 <!ENTITY staff.cat.z3950.marc_view.label "MARC View">
 <!ENTITY staff.cat.z3950.marc_view.accesskey "V">
-<!ENTITY staff.cat.z3950.marc_import_overlay.label "MARC Editor for Overlay">
+<!ENTITY staff.cat.z3950.marc_editor.label "MARC Editor">
+<!ENTITY staff.cat.z3950.marc_editor.accesskey "E">
+<!ENTITY staff.cat.z3950.marc_import_overlay.label "Overlay">
 <!ENTITY staff.cat.z3950.marc_import_overlay.accesskey "O">
-<!ENTITY staff.cat.z3950.result_message.marc_import.label "MARC Editor for Import">
+<!ENTITY staff.cat.z3950.result_message.marc_import.label "Import">
 <!ENTITY staff.cat.z3950.result_message.marc_import.accesskey "I">
 <!ENTITY staff.pat.barcode_entry.retrieve_patron.label "Retrieve Patron">
 <!ENTITY staff.pat.barcode_entry.barcode.label "Barcode:">
@@ -2991,6 +2998,8 @@
 <!ENTITY staff.patron.holds_overlay.print.accesskey "P">
 <!ENTITY staff.patron.holds_overlay.print_full_pull_list.label "Print Full Pull List">
 <!ENTITY staff.patron.holds_overlay.print_full_pull_list.accesskey "u">
+<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.label "Print Full Pull List (Alternate strategy)">
+<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.accesskey "y">
 <!ENTITY staff.patron.holds_overlay.place_hold.label "Place Hold">
 <!ENTITY staff.patron.holds_overlay.place_hold.accesskey "H">
 <!ENTITY staff.patron.holds_overlay.show_cancelled_holds.label "Show Cancelled Holds">

Modified: branches/serials-integration/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_summary.xml
===================================================================
--- branches/serials-integration/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_summary.xml	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_summary.xml	2010-10-18 14:05:06 UTC (rev 18372)
@@ -135,7 +135,7 @@
 				name="serial_holdings_label"
 				class="result_table_title_cell hide_me"
 				type="opac/slot-data"
-				query="datafield[tag=901] subfield[code=c]">
+				query="datafield[tag='901'] subfield[code='c']">
 				<td colspan="2">Issues Held: ${holdingsStatement}
 					<span class="hide_me" name="holdingsStatement" type="opac/template-value"><![CDATA[
 						if (fetchOrgSettingDefault(

Modified: branches/serials-integration/Open-ILS/web/templates/base.tt2
===================================================================
--- branches/serials-integration/Open-ILS/web/templates/base.tt2	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/templates/base.tt2	2010-10-18 14:05:06 UTC (rev 18372)
@@ -21,7 +21,7 @@
 'acqligad','acqliuad','acqlipad','acqphsm','acqlilad','acqedi','acqedim','acqdf','acqdfe','acqdfa','acqda','cnal',
 'acqclt','acqclet','acqcl','acqcle','acqscl','acqscle','acqclp','acqclpa','acqlisum','acqft','acqftm','actsce','actscecm',
 'jub','sdist','ssub','sstr','scap','bre','siss','act', 'acpl', 'ccm', 'aiit', 'atevdef', 'ath', 'atreact','atclean','atenv','atevparam','atcol','actsc','cit',
-'atval','crahp','crmf','crrf','crcd','cust','coust','cgf','czs','cbt','csp','brt','brsrc','bra','bram','brav','vaq','vbq','vqar','ccmm','ccmcmtm','citm','cifm','cvrfm']};
+'atval','crahp','crmf','crrf','crcd','cust','coust','cgf','czs','cbt','csp','brt','brsrc','bra','bram','brav','vaq','vbq','vqar','ccmm','ccmcmtm','citm','cifm','cvrfm','chmm']};
         </script>
         <script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/dojo/dojo.js"></script>
         <script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/dojo/openils_dojo.js"></script>

Modified: branches/serials-integration/Open-ILS/web/templates/default/acq/po/view.tt2
===================================================================
--- branches/serials-integration/Open-ILS/web/templates/default/acq/po/view.tt2	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/templates/default/acq/po/view.tt2	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1,4 +1,5 @@
 [% WRAPPER 'default/base.tt2' %]
+[% ctx.page_title = "Purchase Order" %]
 <script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/common/base64.js"></script>
 <script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/acq/po/view_po.js'></script>
 <script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/po/item_table.js"></script>

Modified: branches/serials-integration/Open-ILS/web/templates/default/actor/user/register_table.tt2
===================================================================
--- branches/serials-integration/Open-ILS/web/templates/default/actor/user/register_table.tt2	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/templates/default/actor/user/register_table.tt2	2010-10-18 14:05:06 UTC (rev 18372)
@@ -81,7 +81,7 @@
     </tr>
 
 
-    <tr fmclass='aua' fmfield='address_type' type='addr-template' required='show'/>
+    <tr fmclass='aua' fmfield='address_type' type='addr-template' required='required'/>
     <tr fmclass='aua' fmfield='post_code' type='addr-template' required='required'/>
     <tr fmclass='aua' fmfield='street1' type='addr-template' required='required'/>
     <tr fmclass='aua' fmfield='street2' type='addr-template' required='show'/>

Modified: branches/serials-integration/Open-ILS/web/templates/default/circ/selfcheck/audio_config.tt2
===================================================================
--- branches/serials-integration/Open-ILS/web/templates/default/circ/selfcheck/audio_config.tt2	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/templates/default/circ/selfcheck/audio_config.tt2	2010-10-18 14:05:06 UTC (rev 18372)
@@ -6,8 +6,8 @@
 <script type="text/javascript">
     SelfCheckManager.audioConfig = {
         'login-success' : '',
-        'login-failure' : '[% ctx.media_prefix %]/audio/circ/question.wav',
-        'checkout-success' : '[% ctx.media_prefix %]/audio/circ/bonus.wav',
-        'checkout-failure' : '[% ctx.media_prefix %]/audio/circ/question.wav',
+        'login-failure' : '[% ctx.media_prefix %]/audio/question.wav',
+        'checkout-success' : '[% ctx.media_prefix %]/audio/bonus.wav',
+        'checkout-failure' : '[% ctx.media_prefix %]/audio/question.wav',
     }
 </script>

Modified: branches/serials-integration/Open-ILS/web/templates/default/conify/global/action_trigger/event_definition.tt2
===================================================================
--- branches/serials-integration/Open-ILS/web/templates/default/conify/global/action_trigger/event_definition.tt2	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/templates/default/conify/global/action_trigger/event_definition.tt2	2010-10-18 14:05:06 UTC (rev 18372)
@@ -28,16 +28,15 @@
         <div dojoType="dijit.layout.ContentPane" layoutAlign="client" style='height:600px'>
             <table  jsId="edGrid" 
                     dojoType="openils.widget.AutoGrid" 
-                    fieldOrder="['owner', 'name', 'hook', 'active', 'delay', 'delay_field', 'group_field', 'validator', 'reactor']"
-                    suppressFields="['template', 'cleanup_failure', 'cleanup_success']"
+                    fieldOrder="['owner', 'name', 'hook', 'active', 'delay', 'delay_field', 'group_field', 'reactor', 'validator']"
+                    suppressFields="['usr_field', 'opt_in_setting', 'max_delay', 'template', 'cleanup_failure', 'cleanup_success']"
                     query="{id: '*'}" 
                     fmClass='atevdef'
-                    defaultCellWidth='"auto"'
                     editStyle='pane'
                     showPaginator='true'
                     editOnEnter='true'>
                 <thead>
-                    <tr><th field='name' get='getEventDefNameLink' formatter='formatEventDefNameLink'/></tr>
+                    <tr><th field='name' width='15%' get='getEventDefNameLink' formatter='formatEventDefNameLink'/></tr>
                 </thead>
             </table>
         </div>

Modified: branches/serials-integration/Open-ILS/web/templates/default/conify/global/config/hold_matrix_matchpoint.tt2
===================================================================
--- branches/serials-integration/Open-ILS/web/templates/default/conify/global/config/hold_matrix_matchpoint.tt2	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/templates/default/conify/global/config/hold_matrix_matchpoint.tt2	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,8 @@
     <table  jsId="hmGrid" 
             autoHeight='true'
             dojoType="openils.widget.AutoGrid" 
-            fieldOrder="['id', 'user_home_ou', 'request_ou', 'pickup_ou', 'item_owning_ou', 'item_circ_ou', 'usr_grp', 'requestor_grp', 'circ_modifier']"
+            fieldOrder="['id', 'strict_ou_match', 'user_home_ou', 'request_ou', 'pickup_ou', 'item_owning_ou', 'item_circ_ou', 'requestor_grp', 'circ_modifier']"
+            suppressFields="['usr_grp']"
             defaultCellWidth='"auto"'
             query="{id: '*'}" 
             fmClass='chmm' 

Modified: branches/serials-integration/Open-ILS/web/templates/default/vandelay/inc/item_attrs.tt2
===================================================================
--- branches/serials-integration/Open-ILS/web/templates/default/vandelay/inc/item_attrs.tt2	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/web/templates/default/vandelay/inc/item_attrs.tt2	2010-10-18 14:05:06 UTC (rev 18372)
@@ -6,7 +6,7 @@
     <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'>
         <div>Import Item Attribute Definitions</div>
         <div>
-            <button dojoType='dijit.form.Button' onClick='itemAttrGrid.showCreateDialog()'>New Definition</button>
+            <button dojoType='dijit.form.Button' onClick='itemAttrGrid.showCreatePane()'>New Definition</button>
             <button dojoType='dijit.form.Button' onClick='itemAttrGrid.deleteSelected()'>Delete Selected</button>
         </div>
     </div>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -186,6 +186,8 @@
                 break;
                 case 'actsc':
                     found = obj.network.simple_request('FM_ACTSC_RETRIEVE_VIA_PCRUD',[ ses(), { 'id' : { '=' : value } }]);
+                    if (typeof found.ilsevent != 'undefined') throw(found);
+                    found = found[0];
                 break;
                 default: return undefined; break;
             }

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -94,6 +94,9 @@
                             if (ev.explicitOriginalTarget != node) return;
                         } else {
                             target = ev.target;
+                            if (target == window) {
+                                target = window.document.documentElement;
+                            }
                         }
                         var filename = location.pathname.split('/')[ location.pathname.split('/').length - 1 ];
                         var base_key = 'oils_persist_' + String(location.hostname + '_' + filename + '_' + target.getAttribute('id')).replace('/','_','g') + '_' + base_key_suffix;
@@ -108,11 +111,21 @@
                             } else if ( attribute_list[j] == 'value' && ['textbox'].indexOf( target.nodeName ) > -1 ) {
                                 value = target.value;
                                 dump('\t' + value + ' <== .' + attribute_list[j] + '\n');
+                            } else if ( attribute_list[j] == 'sizemode' && ['window'].indexOf( target.nodeName ) > -1 ) {
+                                value = window.windowState;
+                                dump('\t' + value + ' <== window.windowState, @' + attribute_list[j] + '\n');
+                            } else if ( attribute_list[j] == 'height' && ['window'].indexOf( target.nodeName ) > -1 ) {
+                                value = window.outerHeight;
+                                dump('\t' + value + ' <== window.outerHeight, @' + attribute_list[j] + '\n');
+                            } else if ( attribute_list[j] == 'width' && ['window'].indexOf( target.nodeName ) > -1 ) {
+                                value = window.outerWidth;
+                                dump('\t' + value + ' <== window.outerWidth, @' + attribute_list[j] + '\n');
                             } else {
                                 dump('\t' + value + ' <== @' + attribute_list[j] + '\n');
                             }
                             prefs.setCharPref( key, value );
-                            // TODO: Need to add logic for window resizing, splitter repositioning, grippy state, etc.
+                            // TODO: Need to add logic for splitter repositioning, grippy state, etc.
+                            // NOTE: oils_persist_peers and oils_persist="width" on those peers can help with the elements adjacent to a splitter
                         }
                         if (target.hasAttribute('oils_persist_peers') && ! ev.cancelable) { // We abuse the .cancelable field on the oils_persist event to prevent looping
                             var peer_list = target.getAttribute('oils_persist_peers').split(' ');
@@ -152,6 +165,22 @@
                         } else if ( attribute_list[j] == 'value' && ['textbox'].indexOf( nodes[i].nodeName ) > -1 ) {
                             nodes[i].value = value;
                             dump('\t' + value + ' ==> .' + attribute_list[j] + '\n');
+                        } else if ( attribute_list[j] == 'sizemode' && ['window'].indexOf( nodes[i].nodeName ) > -1 ) {
+                            switch(value) {
+                                case window.STATE_MAXIMIZED:
+                                    window.maximize();
+                                    break;
+                                case window.STATE_MINIMIZED:
+                                    window.minimize();
+                                    break;
+                            };
+                            dump('\t' + value + ' ==> window.windowState, @' + attribute_list[j] + '\n');
+                        } else if ( attribute_list[j] == 'height' && ['window'].indexOf( nodes[i].nodeName ) > -1 ) {
+                            window.outerHeight = value;
+                            dump('\t' + value + ' ==> window.outerHeight, @' + attribute_list[j] + '\n');
+                        } else if ( attribute_list[j] == 'width' && ['window'].indexOf( nodes[i].nodeName ) > -1 ) {
+                            window.outerWidth = value;
+                            dump('\t' + value + ' ==> window.outerWidth, @' + attribute_list[j] + '\n');
                         } else {
                             nodes[i].setAttribute( attribute_list[j], value);
                             dump('\t' + value + ' ==> @' + attribute_list[j] + '\n');
@@ -191,29 +220,33 @@
                         false
                     );
                 } else {
+                    var node = nodes[i];
                     var event_types = [];
-                    if (nodes[i].hasAttribute('oils_persist_events')) {
-                        var event_type_list = nodes[i].getAttribute('oils_persist_events').split(' ');
+                    if (node.hasAttribute('oils_persist_events')) {
+                        var event_type_list = node.getAttribute('oils_persist_events').split(' ');
                         for (var j = 0; j < event_type_list.length; j++) {
                             event_types.push( event_type_list[j] );
                         }
                     } else {
-                        if (nodes[i].nodeName == 'textbox') { 
+                        if (node.nodeName == 'textbox') { 
                             event_types.push('change'); 
+                        } else if (node.nodeName == 'window') {
+                            event_types.push('resize'); 
+                            node = window; // xul window is an element of window.document
                         } else {
                             event_types.push('command'); 
                         }
                     }
                     for (var j = 0; j < event_types.length; j++) {
-                        nodes[i].addEventListener(
+                        node.addEventListener(
                             event_types[j],
-                            gen_event_handler(event_types[j],nodes[i]),
+                            gen_event_handler(event_types[j],node),
                             false
                         );
                     }
-                    nodes[i].addEventListener(
+                    node.addEventListener(
                         'oils_persist',
-                        gen_oils_persist_handler( base_key, nodes[i] ),
+                        gen_oils_persist_handler( base_key, node ),
                         false
                     );
                 }

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/auth/session.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/auth/session.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/auth/session.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -115,6 +115,13 @@
     'close' : function () { 
         var obj = this;
         obj.error.sdump('D_AUTH','auth.session.close()\n'); 
+        try {
+            netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+            Components.classes["@mozilla.org/cookiemanager;1"]
+                .getService(Components.interfaces.nsICookieManager).removeAll();
+        } catch(E) {
+            dump('Error in auth/session.js, close(): ' + E + '\n');
+        }
         if (obj.key) obj.network.request(
             api.AUTH_DELETE.app,
             api.AUTH_DELETE.method,

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/cat/opac.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/cat/opac.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/cat/opac.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -497,7 +497,7 @@
         JSAN.use('util.window'); var win = new util.window();
         win.open(
             xulG.url_prefix(urls.XUL_SERIAL_SELECT_AOU),
-            'sel_bucket_win' + win.window_name_increment(),
+            '_blank',
             'chrome,resizable,modal,centerscreen'
         );
         if (!g.data.create_mfhd_aou) {
@@ -634,7 +634,7 @@
     JSAN.use('util.window'); var win = new util.window();
     win.open(
         xulG.url_prefix(urls.XUL_RECORD_BUCKETS_QUICK),
-        'sel_bucket_win' + win.window_name_increment(),
+        '_blank',
         'chrome,resizable,modal,centerscreen',
         {
             record_ids: [ docid ]

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/circ/offline.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/circ/offline.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/circ/offline.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -17,6 +17,7 @@
 
 <window id="offline_win" sizemode="maximized"
     onload="try { my_init(); } catch(E) { alert(E); }"
+    windowtype="eg_offline"
     xmlns:html="http://www.w3.org/1999/xhtml"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/constants.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/constants.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/constants.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -351,6 +351,9 @@
     'browser' : '/opac/' + LOCALE + '/skin/default/xml/advanced.xml?nps=1',
     'fieldmapper' : '/opac/common/js/fmall.js',
     'xsl_marc2html' : '/opac/extras/xsl/oilsMARC21slim2HTML.xsl',
+    'ac_jacket_small' : '/opac/extras/ac/jacket/small/',
+    'ac_jacket_large' : '/opac/extras/ac/jacket/large/',
+    'MARC_BATCH_EDIT' : '/opac/extras/merge_template/',
 
     'AUDIO_good' : '/xul/server/skin/media/audio/bonus.wav',
     'AUDIO_bad' : '/xul/server/skin/media/audio/question.wav',
@@ -362,6 +365,7 @@
     'AUTHORITY_MANAGE' : '/eg/cat/authority/list',
     'XUL_AUTH_SIMPLE' : '/xul/server/main/simple_auth.xul',
     'XUL_BIB_BRIEF' : '/xul/server/cat/bib_brief.xul',
+    'XUL_BIB_BRIEF_VERTICAL' : '/xul/server/cat/bib_brief_vertical.xul',
     'XUL_BROWSER' : 'chrome://open_ils_staff_client/content/util/browser.xul',
     'XUL_CHECKIN' : '/xul/server/circ/checkin.xul',
     'XUL_BACKDATE' : '/xul/server/circ/backdate_post_checkin.xul',

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -4,6 +4,9 @@
 var xulG;
 var offlineStrings;
 var authStrings;
+var openTabs = new Array();
+var tempWindow = null;
+var tempFocusWindow = null;
 
 function grant_perms(url) {
     var perms = "UniversalXPConnect UniversalPreferencesWrite UniversalBrowserWrite UniversalPreferencesRead UniversalBrowserRead UniversalFileRead";
@@ -71,10 +74,126 @@
     );
 };
 
+function new_tabs(aTabList, aContinue) {
+    if(aTabList != null) {
+        openTabs = openTabs.concat(aTabList);
+    }
+    if(G.data.session) { // Just add to the list of stuff to open unless we are logged in
+        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+        var targetwindow = null;
+        var focuswindow = null;
+        var focustab = {'focus' : true};
+        if(aContinue == true && tempWindow.closed == false) {
+            if(tempWindow.g == undefined || tempWindow.g.menu == undefined) {
+                setTimeout(
+                    function() {
+                        new_tabs(null, true);
+                    }, 300);
+                return null;
+            }
+            targetwindow = tempWindow;
+            tempWindow = null;
+            focuswindow = tempFocusWindow;
+            tempFocusWindow = null;
+            focustab = {'nofocus' : true};
+        }
+        else if(tempWindow != null) { // In theory, we are waiting on a setTimeout
+            if(tempWindow.closed == true) // But someone closed our window?
+            {
+                tempWindow = null;
+                tempFocusWindow = null;
+            }
+            else
+            {
+                return null;
+            }
+        }
+        var newTab;
+        var firstURL;
+        var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+            getService(Components.interfaces.nsIWindowMediator);
+            // This may look out of place, but this is so we can continue this loop from down below
+opentabs:
+            while(openTabs.length > 0) {
+            newTab = openTabs.shift();
+            if(newTab == 'new' || newTab == 'init') {
+                if(newTab != 'init' && openTabs.length > 0 && openTabs[0] != 'new') {
+                    firstURL = openTabs.shift();
+                    if(firstURL != 'tab') { // 'new' followed by 'tab' should be equal to 'init' in functionality, this should do that
+                        if(urls[firstURL]) {
+                            firstURL = urls[firstURL];
+                        }
+                        firstURL = '&firstURL=' + window.escape(firstURL);
+                    }
+                    else {
+                        firstURL = '';
+                    }
+                }
+                else {
+                    firstURL = '';
+                }
+                targetwindow = xulG.window.open(urls.XUL_MENU_FRAME
+                    + '?server='+window.escape(G.data.server) + firstURL,
+                    '_blank','chrome,resizable'
+                );
+                targetwindow.xulG = xulG;
+                if (focuswindow == null) {
+                    focuswindow = targetwindow;
+                }
+                tempWindow = targetwindow;
+                tempFocusWindow = focuswindow;
+                setTimeout(
+                    function() {
+                        new_tabs(null, true);
+                    }, 300);
+                return null;
+            }
+            else {
+                if(newTab == 'tab') {
+                    newTab = null;
+                }
+                else if(urls[newTab]) {
+                    newTab = urls[newTab];
+                }
+                if(targetwindow != null) { // Already have a previous target window? Use it first.
+                    if(targetwindow.g.menu.new_tab(newTab,focustab,null)) {
+                        focustab = {'nofocus' : true};
+                        continue;
+                    }
+                }
+                var enumerator = wm.getEnumerator('eg_menu');
+                while(enumerator.hasMoreElements()) {
+                    targetwindow = enumerator.getNext();
+                    if(targetwindow.g.menu.new_tab(newTab,focustab,null)) {
+                        focustab = {'nofocus' : true};
+                        if (focuswindow == null) {
+                            focuswindow = targetwindow;
+                        }
+                        continue opentabs;
+                    }
+                }
+                // No windows found to add the tab to? Make a new one.
+                if(newTab == null) { // Were we making a "default" tab?
+                    openTabs.unshift('init'); // 'init' does that for us!
+                }
+                else {
+                    openTabs.unshift('new',newTab);
+                }
+            }
+        }
+        if(focuswindow != null) {
+            focuswindow.focus();
+        }
+    }
+}
+
 function main_init() {
     dump('entering main_init()\n');
     try {
         clear_the_cache();
+        if("arguments" in window && window.arguments.length > 0 && window.arguments[0].wrappedJSObject != undefined && window.arguments[0].wrappedJSObject.openTabs != undefined) {
+            openTabs = openTabs.concat(window.arguments[0].wrappedJSObject.openTabs);
+        }
 
         // Now we can safely load the strings without the cache getting wiped
         offlineStrings = document.getElementById('offlineStrings');
@@ -353,15 +472,7 @@
             'command',
             function() {
                 if (G.data.session) {
-                    try {
-                        //G.data_xul.g.open_menu();
-                        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-                        var mframe = xulG.window.open(( String(urls.XUL_MENU_FRAME).match(/^chrome:/) ? '' : G.data.server ) + urls.XUL_MENU_FRAME
-                            + '?server='+window.escape(G.data.server),
-                            'main'+xulG.window.window_name_increment(),'chrome,resizable'
-                        );
-                        mframe.xulG = xulG; 
-                    } catch(E) { alert(E); }
+                    new_tabs(Array('new'), null, null);
                 } else {
                     alert ( offlineStrings.getString('main.new_window_btn.login_first_warning') );
                 }

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/main.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -21,7 +21,7 @@
 <window id="main_win" 
     onload="try { main_init(); } catch(E) { alert(E); }"
     onunload="try { G.auth.logoff(); } catch(E) { alert(E); }"
-    title="&staff.auth.title;"
+    title="&staff.auth.title;" persist="width height sizemode"
     width="640" height="480" windowtype="eg_main"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -27,18 +27,6 @@
         },
         false
     );
-
-    if (xulG.pref.getBoolPref('open-ils.disable_accesskeys_on_tabs')) {
-        var tabs = document.getElementById('main_tabs');
-        for (var i = 0; i < tabs.childNodes.length; i++) {
-            tabs.childNodes[i].setAttribute('accesskey','');
-        }
-    }
-
-    if (xulG.pref.getBoolPref('open-ils.enable_join_tabs')) {
-        document.getElementById('join_tabs_menuitem_vertical').hidden = false;
-        document.getElementById('join_tabs_menuitem_horizontal').hidden = false;
-    }
 }
 
 main.menu.prototype = {
@@ -145,30 +133,25 @@
             'cmd_new_window' : [
                 ['oncommand'],
                 function() {
-                    obj.data.stash_retrieve();
-                    var mframe = obj.window.open(
-                        obj.url_prefix(urls.XUL_MENU_FRAME)
-                        + '?server='+window.escape(urls.remote),
-                        'main' + obj.window.window_name_increment(),
-                        'chrome,resizable'); 
-                    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-                    mframe.xulG = xulG;
-                    /* This window should get its own objects for these */
-                    delete mframe.xulG['_data'];
+                    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+                        getService(Components.interfaces.nsIWindowMediator);
+                    wm.getMostRecentWindow('eg_main').new_tabs(Array('new'));
                 }
             ],
             'cmd_new_tab' : [
                 ['oncommand'],
-                function() { obj.new_tab(null,{'focus':true},null); }
+                function() {
+                    if (obj.new_tab(null,{'focus':true},null) == false)
+                    {
+                        if(window.confirm(offlineStrings.getString('menu.new_tab.max_tab_dialog')))
+                        {
+                            var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+                                getService(Components.interfaces.nsIWindowMediator);
+                            wm.getMostRecentWindow('eg_main').new_tabs(Array('tab'));
+                        }
+                    }
+                }
             ],
-            'cmd_join_tabs_vertical' : [
-                ['oncommand'],
-                function() { obj.join_tabs({'orient':'vertical'}); }
-            ],
-            'cmd_join_tabs_horizontal' : [
-                ['oncommand'],
-                function() { obj.join_tabs({'orient':'horizontal'}); }
-            ],
             'cmd_close_tab' : [
                 ['oncommand'],
                 function() { obj.close_tab(); }
@@ -1042,6 +1025,18 @@
                 }
             ],
 
+            'cmd_marc_batch_edit' : [
+                ['oncommand'],
+                function() {
+                    obj.set_tab(
+                        obj.url_prefix(urls.MARC_BATCH_EDIT),{
+                            'tab_name' : offlineStrings.getString('menu.cmd_marc_batch_edit.tab')
+                        },
+                        {}
+                    );
+                }
+            ],
+
             /* Admin menu */
             'cmd_change_session' : [
                 ['oncommand'],
@@ -1283,7 +1278,6 @@
                     }
                 }
             ],
-
         };
 
         JSAN.use('util.controller');
@@ -1292,12 +1286,20 @@
         obj.controller.init( { 'window_knows_me_by' : 'g.menu.controller', 'control_map' : cmd_map } );
 
         obj.controller.view.tabbox = window.document.getElementById('main_tabbox');
-        obj.controller.view.tabs = obj.controller.view.tabbox.firstChild;
-        obj.controller.view.panels = obj.controller.view.tabbox.lastChild;
-
-        obj.new_tab(null,{'focus':true},null);
-
-        obj.init_tab_focus_handlers();
+        // Despite what the docs say:
+        // The "tabs" element need not be the first child
+        // The "panels" element need not be the second/last
+        // Nor need they be the only ones there.
+        // Thus, use the IDs for robustness.
+        obj.controller.view.tabs = window.document.getElementById('main_tabs');
+        obj.controller.view.panels = window.document.getElementById('main_panels');
+        obj.controller.view.tabscroller = window.document.getElementById('main_tabs_scrollbox');
+        if(params['firstURL']) {
+            obj.new_tab(params['firstURL'],{'focus':true},null);
+        }
+        else {
+            obj.new_tab(null,{'focus':true},null);
+        }
     },
 
     'spawn_search' : function(s) {
@@ -1306,55 +1308,11 @@
         obj.new_patron_tab( {}, { 'doit' : 1, 'query' : js2JSON(s) } );
     },
 
-    'init_tab_focus_handlers' : function() {
-        var obj = this;
-        for (var i = 0; i < obj.controller.view.tabs.childNodes.length; i++) {
-            var tab = obj.controller.view.tabs.childNodes[i];
-            var panel = obj.controller.view.panels.childNodes[i];
-            tab.addEventListener(
-                'command',
-                function(p) {
-                    return function() {
-                        try {
-                            netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-                            if (p
-                                && p.firstChild 
-                                && ( p.firstChild.nodeName == 'iframe' || p.firstChild.nodeName == 'browser' )
-                                && p.firstChild.contentWindow 
-                            ) {
-                                var cw = p.firstChild.contentWindow;
-                                var help_params = {
-                                    'protocol' : cw.location.protocol,
-                                    'hostname' : cw.location.hostname,
-                                    'port' : cw.location.port,
-                                    'pathname' : cw.location.pathname,
-                                    'src' : ''
-                                };
-                                obj.set_help_context(help_params);
-                                if (typeof cw.default_focus == 'function') {
-                                    cw.default_focus();
-                                }
-                            }
-                        } catch(E) {
-                            obj.error.sdump('D_ERROR','init_tab_focus_handler: ' + js2JSON(E));
-                        }
-                    }
-                }(panel),
-                false
-            );
-        }
-    },
-
-    // We keep a reference to content_params fed to tabs, so if we manipulate the DOM (say, via join_tabs),
-    // we can re-inject the content_params into the content if needed.  We have to watch out for memory leaks
-    // doing this.
-    'preserved_content_params' : {},
-
     'close_all_tabs' : function() {
         var obj = this;
         try {
             var count = obj.controller.view.tabs.childNodes.length;
-            for (var i = 0; i < count; i++) obj.close_tab();
+            for (var i = 1; i < count; i++) obj.close_tab();
             setTimeout( function(){ obj.controller.view.tabs.firstChild.focus(); }, 0);
         } catch(E) {
             obj.error.standard_unexpected_error_alert(offlineStrings.getString('menu.close_all_tabs.error'),E);
@@ -1363,154 +1321,101 @@
 
     'close_tab' : function (specific_idx) {
         var idx = specific_idx || this.controller.view.tabs.selectedIndex;
-        var tab = this.controller.view.tabs.childNodes[idx];
         var panel = this.controller.view.panels.childNodes[ idx ];
-        while ( panel.lastChild ) panel.removeChild( panel.lastChild );
-        if (idx == 0) {
-            try {
-                this.controller.view.tabs.advanceSelectedTab(+1);
-            } catch(E) {
-                this.error.sdump('D_TAB','failed tabs.advanceSelectedTab(+1):'+js2JSON(E) + '\n');
-                try {
-                    this.controller.view.tabs.advanceSelectedTab(-1);
-                } catch(E) {
-                    this.error.sdump('D_TAB','failed again tabs.advanceSelectedTab(-1):'+
-                        js2JSON(E) + '\n');
-                }
-            }
-        } else {
-            try {
-                this.controller.view.tabs.advanceSelectedTab(-1);
-            } catch(E) {
-                this.error.sdump('D_TAB','failed tabs.advanceSelectedTab(-1):'+js2JSON(E) + '\n');
-                try {
-                    this.controller.view.tabs.advanceSelectedTab(+1);
-                } catch(E) {
-                    this.error.sdump('D_TAB','failed again tabs.advanceSelectedTab(+1):'+
-                        js2JSON(E) + '\n');
-                }
-            }
-
+        this.controller.view.tabs.removeItemAt(idx);
+        this.controller.view.panels.removeChild(panel);
+        if(this.controller.view.tabs.childNodes.length > idx) {
+            this.controller.view.tabbox.selectedIndex = idx;
         }
-        
-        this.error.sdump('D_TAB','\tnew tabbox.selectedIndex = ' + this.controller.view.tabbox.selectedIndex + '\n');
-
-        this.controller.view.tabs.childNodes[ idx ].hidden = true;
-        this.error.sdump('D_TAB','tabs.childNodes[ ' + idx + ' ].hidden = true;\n');
-
-        // Make sure we keep at least one tab open.
-        var tab_flag = true;
-        for (var i = 0; i < this.controller.view.tabs.childNodes.length; i++) {
-            var tab = this.controller.view.tabs.childNodes[i];
-            if (!tab.hidden)
-                tab_flag = false;
+        else {
+            this.controller.view.tabbox.selectedIndex = idx - 1;
         }
-        if (tab_flag) {
-            this.controller.view.tabs.selectedIndex = 0;
+        this.controller.view.tabscroller.ensureElementIsVisible(this.controller.view.tabs.selectedItem);
+        this.update_all_tab_names();
+        // Make sure we keep at least one tab open.
+        if(this.controller.view.tabs.childNodes.length == 1) {
             this.new_tab(); 
         }
     },
-
-    'join_tabs' : function(params) {
-        try {
-            if (!params) { params = {}; }
-            if (!params.orient) { params.orient = 'horizontal'; }
-
-            var left_idx = params.specific_idx || this.controller.view.tabs.selectedIndex;
-            var left_tab = this.controller.view.tabs.childNodes[left_idx];
-            var left_panel = this.controller.view.panels.childNodes[ left_idx ];
-
-            // Find next not-hidden tab
-            var right_idx;
-            for (var i = left_idx + 1; i<this.controller.view.tabs.childNodes.length; i++) {
-                var tab = this.controller.view.tabs.childNodes[i];
-                if (!tab.hidden && !right_idx) {
-                    right_idx = i;
-                }
+    
+    'update_all_tab_names' : function() {
+        var doAccessKeys = !xulG.pref.getBoolPref('open-ils.disable_accesskeys_on_tabs');
+        for(var i = 1; i < this.controller.view.tabs.childNodes.length; ++i) {
+            var tab = this.controller.view.tabs.childNodes[i];
+            tab.curindex = i;
+            tab.label = i + ' ' + tab.origlabel;
+            if(doAccessKeys && offlineStrings.testString('menu.tab' + i + '.accesskey')) {
+                tab.accessKey = offlineStrings.getString('menu.tab' + i + '.accesskey');
             }
-            if (!right_idx) { return; }
-
-            // Grab the content
-            var right_tab = this.controller.view.tabs.childNodes[right_idx];
-            var right_panel = this.controller.view.panels.childNodes[ right_idx ];
-
-            var left_content = left_panel.removeChild( left_panel.firstChild );
-            var right_content = right_panel.removeChild( right_panel.firstChild );
-
-            // Create a wrapper and shuffle the content
-            var box = params.orient == 'vertical' ? document.createElement('vbox') : document.createElement('hbox');
-            box.setAttribute('flex',1);
-            left_panel.appendChild(box);
-            box.appendChild(left_content);
-            var splitter = document.createElement('splitter');
-            splitter.appendChild( document.createElement('grippy') );
-            box.appendChild(splitter);
-            box.appendChild(right_content);
-
-            right_tab.hidden = true;
-            // FIXME: if we really want to combine labels for joined tabs, need to handle the cases where the content dynamically set their tab 
-            // labels with xulG.set_tab_name
-            left_tab.setAttribute('unadornedlabel', left_tab.getAttribute('unadornedlabel') + ' / ' + right_tab.getAttribute('unadornedlabel'));
-            left_tab.setAttribute('label', left_tab.getAttribute('label') + ' / ' + right_tab.getAttribute('unadornedlabel'));
-
-            // Re-apply content params, etc.
-            var left_params = this.preserved_content_params[ left_idx ];
-            var right_params = this.preserved_content_params[ right_idx ];
-            this.preserved_content_params[ left_idx ] = function() {
-                try {
-                    left_params();
-                    right_params();
-                } catch(E) {
-                    alert('Error re-applying content params after join_tabs');
-                }
-            };
-            this.preserved_content_params[ left_idx ]();
-
-        } catch(E) {
-            alert('Error in menu.js with join_tabs(): ' + E);
         }
     },
 
-    'find_free_tab' : function() {
-        var last_not_hidden = -1;
-        for (var i = 0; i<this.controller.view.tabs.childNodes.length; i++) {
-            var tab = this.controller.view.tabs.childNodes[i];
-            if (!tab.hidden)
-                last_not_hidden = i;
+    'new_tab' : function(url,params,content_params) {
+        var obj = this;
+        var max_tabs = 0;
+        try {
+            var max_tabs = xulG.pref.getIntPref('open-ils.window_max_tabs') || max_tabs;
         }
-        if (last_not_hidden == this.controller.view.tabs.childNodes.length - 1)
-            last_not_hidden = -1;
-        // If the one next to last_not_hidden is hidden, we want it.
-        // Basically, we fill in tabs after existing tabs for as 
-        // long as possible.
-        var idx = last_not_hidden + 1;
-        var candidate = this.controller.view.tabs.childNodes[ idx ];
-        if (candidate.hidden)
-            return idx;
-        // Alright, find the first hidden then
-        for (var i = 0; i<this.controller.view.tabs.childNodes.length; i++) {
-            var tab = this.controller.view.tabs.childNodes[i];
-            if (tab.hidden)
-                return i;
+        catch (e) {}
+        if(max_tabs > 0 && this.controller.view.tabs.childNodes.length > max_tabs) return false;
+        var tab = this.w.document.createElement('tab');
+        var panel = this.w.document.createElement('tabpanel');
+        var tabscroller = this.controller.view.tabscroller;
+        this.controller.view.tabs.appendChild(tab);
+        this.controller.view.panels.appendChild(panel);
+        tab.curindex = this.controller.view.tabs.childNodes.length - 1;
+        if(!xulG.pref.getBoolPref('open-ils.disable_accesskeys_on_tabs')) {
+            if(offlineStrings.testString('menu.tab' + tab.curindex + '.accesskey')) {
+                tab.accessKey = offlineStrings.getString('menu.tab' + tab.curindex + '.accesskey');
+            }
         }
-        return -1;
-    },
-
-    'new_tab' : function(url,params,content_params) {
-        var tc = this.find_free_tab();
-        if (tc == -1) { return null; } // 9 tabs max
-        var tab = this.controller.view.tabs.childNodes[ tc ];
-        tab.hidden = false;
+        var tabs = this.controller.view.tabs;
+        tab.addEventListener(
+            'command',
+            function() {
+                try {
+                    tabscroller.ensureElementIsVisible(tab);
+                    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                    if (panel
+                        && panel.firstChild 
+                        && ( panel.firstChild.nodeName == 'iframe' || panel.firstChild.nodeName == 'browser' )
+                        && panel.firstChild.contentWindow 
+                    ) {
+                        var cw = panel.firstChild.contentWindow;
+                        var help_params = {
+                            'protocol' : cw.location.protocol,
+                            'hostname' : cw.location.hostname,
+                            'port' : cw.location.port,
+                            'pathname' : cw.location.pathname,
+                            'src' : ''
+                        };
+                        obj.set_help_context(help_params);
+                        if (typeof cw.default_focus == 'function') {
+                            cw.default_focus();
+                        }
+                    }
+                } catch(E) {
+                    obj.error.sdump('D_ERROR','init_tab_focus_handler: ' + js2JSON(E));
+                }
+            }
+            ,
+            false
+        );
         if (!content_params) content_params = {};
         if (!params) params = {};
         if (!params.tab_name) params.tab_name = offlineStrings.getString('menu.new_tab.tab');
         if (!params.nofocus) params.focus = true; /* make focus the default */
         try {
-            if (params.focus) this.controller.view.tabs.selectedIndex = tc;
-            params.index = tc;
+            if (params.focus) {
+                this.controller.view.tabs.selectedItem = tab;
+                tabscroller.ensureElementIsVisible(tab);
+            }
+            params.index = tab.curindex;
             this.set_tab(url,params,content_params);
+            return true;
         } catch(E) {
             this.error.sdump('D_ERROR',E);
+            return false;
         }
     },
 
@@ -1640,8 +1545,19 @@
             if (params.src) { help_btn.setAttribute('src', params.src); }
         }
     },
-    'augment_content_params' : function(idx,tab,params,content_params) {
+    'set_tab' : function(url,params,content_params) {
         var obj = this;
+        if (!url) url = '/xul/server/';
+        if (!url.match(/:\/\//) && !url.match(/^data:/)) url = urls.remote + url;
+        if (!params) params = {};
+        if (!content_params) content_params = {};
+        var idx = this.controller.view.tabs.selectedIndex;
+        if (params && typeof params.index != 'undefined') idx = params.index;
+        var tab = this.controller.view.tabs.childNodes[ idx ];
+        if (params.focus) tab.focus();
+        var panel = this.controller.view.panels.childNodes[ idx ];
+        while ( panel.lastChild ) panel.removeChild( panel.lastChild );
+
         content_params.new_tab = function(a,b,c) { return obj.new_tab(a,b,c); };
         content_params.set_tab = function(a,b,c) { return obj.set_tab(a,b,c); };
         content_params.close_tab = function() { return obj.close_tab(); };
@@ -1650,7 +1566,7 @@
         content_params.volume_item_creator = function(a) { return obj.volume_item_creator(a); };
         content_params.get_new_session = function(a) { return obj.get_new_session(a); };
         content_params.holdings_maintenance_tab = function(a,b,c) { return obj.holdings_maintenance_tab(a,b,c); };
-        content_params.set_tab_name = function(name) { tab.setAttribute('unadornedlabel',name); tab.setAttribute('label',(idx + 1) + ' ' + name); };
+        content_params.set_tab_name = function(name) { tab.label = tab.curindex + ' ' + name; tab.origlabel = name; };
         content_params.set_help_context = function(params) { return obj.set_help_context(params); };
         content_params.open_chrome_window = function(a,b,c) { return xulG.window.open(a,b,c); };
         content_params.url_prefix = function(url) { return obj.url_prefix(url); };
@@ -1662,24 +1578,6 @@
         };
         content_params.chrome_xulG = xulG;
         content_params._data = xulG._data;
-
-        return content_params;
-    },
-    'set_tab' : function(url,params,content_params) {
-        var obj = this;
-        if (!url) url = '/xul/server/';
-        if (!url.match(/:\/\//) && !url.match(/^data:/)) url = urls.remote + url;
-        if (!params) params = {};
-        if (!content_params) content_params = {};
-        var idx = this.controller.view.tabs.selectedIndex;
-        if (obj.preserved_content_params[idx]) { delete obj.preserved_content_params[ idx ]; }
-        if (params && typeof params.index != 'undefined') idx = params.index;
-        var tab = this.controller.view.tabs.childNodes[ idx ];
-        if (params.focus) tab.focus();
-        var panel = this.controller.view.panels.childNodes[ idx ];
-        while ( panel.lastChild ) panel.removeChild( panel.lastChild );
-
-        content_params = obj.augment_content_params(idx,tab,params,content_params);
         if (params && params.tab_name) content_params.set_tab_name( params.tab_name );
         
         var frame;
@@ -1706,9 +1604,6 @@
                             'passthru_content_params' : content_params,
                         }
                     );
-                    obj.preserved_content_params[ idx ] = function() {
-                        b.passthru_content_params = content_params;
-                    }
                 } catch(E) {
                     alert(E);
                 }
@@ -1718,40 +1613,37 @@
                 panel.appendChild(frame);
                 dump('creating iframe with src = ' + url + '\n');
                 frame.setAttribute('src',url);
-                obj.preserved_content_params[ idx ] = function() {
-                    try {
-                        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-                        var cw = frame.contentWindow;
-                        if (typeof cw.wrappedJSObject != 'undefined') cw = cw.wrappedJSObject;
-                        cw.IAMXUL = true;
-                        cw.xulG = content_params;
-                        cw.addEventListener(
-                            'load',
-                            function() {
-                                try {
-                                    if (typeof cw.help_context_set_locally == 'undefined') {
-                                        var help_params = {
-                                            'protocol' : cw.location.protocol,
-                                            'hostname' : cw.location.hostname,
-                                            'port' : cw.location.port,
-                                            'pathname' : cw.location.pathname,
-                                            'src' : ''
-                                        };
-                                        obj.set_help_context(help_params);
-                                    } else if (typeof cw.default_focus == 'function') {
-                                        cw.default_focus();
-                                    }
-                                } catch(E) {
-                                    obj.error.sdump('D_ERROR', 'main.menu, set_tab, onload: ' + E);
+                try {
+                    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                    var cw = frame.contentWindow;
+                    if (typeof cw.wrappedJSObject != 'undefined') cw = cw.wrappedJSObject;
+                    cw.IAMXUL = true;
+                    cw.xulG = content_params;
+                    cw.addEventListener(
+                        'load',
+                        function() {
+                            try {
+                                if (typeof cw.help_context_set_locally == 'undefined') {
+                                    var help_params = {
+                                        'protocol' : cw.location.protocol,
+                                        'hostname' : cw.location.hostname,
+                                        'port' : cw.location.port,
+                                        'pathname' : cw.location.pathname,
+                                        'src' : ''
+                                    };
+                                    obj.set_help_context(help_params);
+                                } else if (typeof cw.default_focus == 'function') {
+                                    cw.default_focus();
                                 }
-                            },
-                            false
-                        );
-                    } catch(E) {
-                        this.error.sdump('D_ERROR', 'main.menu: ' + E);
-                    }
-                };
-                obj.preserved_content_params[ idx ]();
+                            } catch(E) {
+                                obj.error.sdump('D_ERROR', 'main.menu, set_tab, onload: ' + E);
+                            }
+                        },
+                        false
+                    );
+                } catch(E) {
+                    this.error.sdump('D_ERROR', 'main.menu: ' + E);
+                }
             }
         } catch(E) {
             this.error.sdump('D_ERROR', 'main.menu:2: ' + E);

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -31,6 +31,7 @@
     onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
     orient="vertical" width="1024" height="740"
     sizemode="maximized" persist="width height sizemode" title="&staff.main.menu.title;"
+    windowtype="eg_menu"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
@@ -86,6 +87,7 @@
                 JSAN.use('main.menu'); g.menu = new main.menu();
                 g.menu.init( { 
                     'server' : g.cgi.param('server'),
+                    'firstURL' : g.cgi.param('firstURL'),
                 } );
 
                 JSAN.use('util.window'); g.window = new util.window();

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -13,8 +13,6 @@
     <command id="cmd_new_tab" key="new-tab-key" />
     <command id="cmd_close_tab" key="close-tab-key" />
     <command id="cmd_close_all_tabs" key="close-all-tabs-key" />
-    <command id="cmd_join_tabs_vertical" label="&staff.main.menu.file.join_tabs_vertical.label;" accesskey="&staff.main.menu.file.join_tabs_vertical.accesskey;" />
-    <command id="cmd_join_tabs_horizontal" label="&staff.main.menu.file.join_tabs_horizontal.label;" accesskey="&staff.main.menu.file.join_tabs_horizontal.accesskey;" />
     <command id="cmd_shutdown" />
 
     <command id="cmd_edit_copy_buckets" />
@@ -50,6 +48,7 @@
     <command id="cmd_print_list_template_edit" />
     <command id="cmd_z39_50_import" />
     <command id="cmd_create_new_marc_book" />
+    <command id="cmd_marc_batch_edit" label="&staff.main.menu.cat.marc_batch_edit.label;" accesskey="&staff.main.menu.cat.marc_batch_edit.accesskey;"/>
     <command id="cmd_replace_barcode" />
     <command id="cmd_reprint" />
     <command id="cmd_retrieve_last_patron" />
@@ -201,8 +200,6 @@
     <menupopup id="main.menu.file.popup">
         <menuitem label="&staff.main.menu.file.new.label;" accesskey="&staff.main.menu.file.new.accesskey;" key="new-window-key" command="cmd_new_window"/>
         <menuitem label="&staff.main.menu.file.new_tab.label;" accesskey="&staff.main.menu.file.new_tab.accesskey;" key="new-tab-key" command="cmd_new_tab"/>
-        <menuitem id="join_tabs_menuitem_horizontal" hidden="true" command="cmd_join_tabs_horizontal"/>
-        <menuitem id="join_tabs_menuitem_vertical" hidden="true" command="cmd_join_tabs_vertical"/>
         <menuseparator />
         <menuitem label="&staff.main.menu.file.close_tab.label;" accesskey="&staff.main.menu.file.close_tab.accesskey;" oldaccesskey="&staff.main.menu.file.close_tab.key;" key="close-tab-key" command="cmd_close_tab"/>
         <menuitem label="&staff.main.menu.tabs.close;" accesskey="&staff.main.menu.tabs.close.accesskey;" key="close-all-tabs-key" command="cmd_close_all_tabs"/>
@@ -275,6 +272,7 @@
         <menuitem label="&staff.main.menu.cat.create_marc.label;" accesskey="&staff.main.menu.cat.create_marc.accesskey;" command="cmd_create_marc"/>
         <menuitem label="&staff.main.menu.cat.z39_50_import.label;" accesskey="&staff.main.menu.cat.z39_50_import.accesskey;" command="cmd_z39_50_import"/>
         <menuitem label="&staff.main.menu.cat.vandelay.label;" command="cmd_open_vandelay"/>
+        <menuitem command="cmd_marc_batch_edit"/>
         <menuseparator />
         <menuitem label="&staff.main.menu.replace_barcode.label;" command="cmd_replace_barcode"/>
         <menuitem label="&staff.main.menu.cat.retrieve_last_record.label;" accesskey="&staff.main.menu.cat.retrieve_last_record.accesskey;" command="cmd_retrieve_last_record" key="retrieve_last_record_key"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_overlay.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -16,27 +16,16 @@
 <box id="menu_frame_main" flex="1" orient="vertical">
     <toolbox id="main_toolbox"/>
     <tabbox id="main_tabbox" flex="1" eventnode="window" handleCtrlTab="true">
-        <tabs id="main_tabs" closebutton="true" onclosetab="g.menu.close_tab()">
-            <tab id="tab_1" accesskey="&staff.chrome.menu_frame_overlay.tab1.accesskey;" label="&staff.chrome.menu_frame_overlay.tab1.label;" hidden="true" />
-            <tab id="tab_2" accesskey="&staff.chrome.menu_frame_overlay.tab2.accesskey;" label="&staff.chrome.menu_frame_overlay.tab2.label;" hidden="true" />
-            <tab id="tab_3" accesskey="&staff.chrome.menu_frame_overlay.tab3.accesskey;" label="&staff.chrome.menu_frame_overlay.tab3.label;" hidden="true" />
-            <tab id="tab_4" accesskey="&staff.chrome.menu_frame_overlay.tab4.accesskey;" label="&staff.chrome.menu_frame_overlay.tab4.label;" hidden="true" />
-            <tab id="tab_5" accesskey="&staff.chrome.menu_frame_overlay.tab5.accesskey;" label="&staff.chrome.menu_frame_overlay.tab5.label;" hidden="true" />
-            <tab id="tab_6" accesskey="&staff.chrome.menu_frame_overlay.tab6.accesskey;" label="&staff.chrome.menu_frame_overlay.tab6.label;" hidden="true" />
-            <tab id="tab_7" accesskey="&staff.chrome.menu_frame_overlay.tab7.accesskey;" label="&staff.chrome.menu_frame_overlay.tab7.label;" hidden="true" />
-            <tab id="tab_8" accesskey="&staff.chrome.menu_frame_overlay.tab8.accesskey;" label="&staff.chrome.menu_frame_overlay.tab8.label;" hidden="true" />
-            <tab id="tab_9" accesskey="&staff.chrome.menu_frame_overlay.tab9.accesskey;" label="&staff.chrome.menu_frame_overlay.tab9.label;" hidden="true" />
-        </tabs>
+        <hbox>
+            <arrowscrollbox orient="horizontal" id="main_tabs_scrollbox" flex="2">
+                <tabs id="main_tabs">
+                    <tab hidden="true" />
+                </tabs>
+            </arrowscrollbox>
+            <toolbarbutton id="main_tabs_closebutton" class="tabs-closebutton close-button" oncommand="g.menu.close_tab()" />
+        </hbox>
         <tabpanels id="main_panels" flex="1">
-            <tabpanel id="panel_1"><label value="panel_1"/></tabpanel>
-            <tabpanel id="panel_2"><label value="panel_2"/></tabpanel>
-            <tabpanel id="panel_3"><label value="panel_3"/></tabpanel>
-            <tabpanel id="panel_4"><label value="panel_4"/></tabpanel>
-            <tabpanel id="panel_5"><label value="panel_5"/></tabpanel>
-            <tabpanel id="panel_6"><label value="panel_6"/></tabpanel>
-            <tabpanel id="panel_7"><label value="panel_7"/></tabpanel>
-            <tabpanel id="panel_8"><label value="panel_8"/></tabpanel>
-            <tabpanel id="panel_9"><label value="panel_9"/></tabpanel>
+            <tabpanel />
         </tabpanels>
     </tabbox>
     <statusbar>
@@ -83,7 +72,7 @@
         <menu id="main.menu.admin" />
         <menu id="main.menu.help" />
     </menubar>
-    <toolbar id="main_toolbar" hidden="true" class="chromeclass-toolbar">
+    <toolbar id="main_toolbar" hidden="true">
         <toolbarbutton id="tb_checkout" 
             command="cmd_circ_checkout" 
             image="chrome://open_ils_staff_client/skin/media/images/Arrow-rightup-small.png" 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/deck.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/deck.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/deck.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1,19 +1,19 @@
 dump('entering util/deck.js\n');
 
 if (typeof util == 'undefined') util = {};
-util.deck = function (id) {
+util.deck = function (id_or_node) {
 
-    this.node = document.getElementById(id);
+    this.node = typeof id_or_node == 'object' ? id_or_node : document.getElementById(id_or_node);
 
     JSAN.use('util.error'); this.error = new util.error();
 
     if (!this.node) {
-        var error = 'util.deck: Could not find element ' + id;
+        var error = 'util.deck: Could not find element ' + id_or_node;
         this.error.sdump('D_ERROR',error);
         throw(error);
     }
     if (this.node.nodeName != 'deck') {
-        var error = 'util.deck: ' + id + 'is not a deck' + "\nIt's a " + this.node.nodeName;
+        var error = 'util.deck: ' + id_or_node + 'is not a deck' + "\nIt's a " + this.node.nodeName;
         this.error.sdump('D_ERROR',error);
         throw(error);
     }

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/list.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/list.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/list.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -305,12 +305,20 @@
             }
             /* local file will trump remote file if allowed, so save ourselves an http request if this is the case */
             if (obj.data.hash.aous['url.remote_column_settings'] && ! my_cols ) {
-                var x = new XMLHttpRequest();
-                var url = obj.data.hash.aous['url.remote_column_settings'] + '/tree_columns_for_' + window.escape(id);
-                x.open("GET", url, false);
-                x.send(null);
-                if (x.status == 200) {
-                    my_cols = JSON2js( x.responseText );
+                try {
+                    var x = new XMLHttpRequest();
+                    var url = obj.data.hash.aous['url.remote_column_settings'] + '/tree_columns_for_' + window.escape(id);
+                    x.open("GET", url, false);
+                    x.send(null);
+                    if (x.status == 200) {
+                        my_cols = JSON2js( x.responseText );
+                    }
+                } catch(E) {
+                    // This can happen in the offline interface if you logged in previously and url.remote_column_settings is set.
+                    // 1) You may be really "offline" now
+                    // 2) the URL may just be a path component without a hostname (ie "/xul/column_settings/"), which won't work
+                    // when appended to chrome://open_ils_staff_client/
+                    dump('Error retrieving column settings from ' + url + ': ' + E + '\n');
                 }
             }
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/network.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/network.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/network.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -490,7 +490,7 @@
             if ( 
                 (typeof result.ilsevent != 'undefined') && 
                 (
-                    (override_params.overridable_events.indexOf( result.ilsevent == null ? null : Number(result.ilsevent) ) != -1) ||
+                    (override_params.overridable_events.indexOf( result.ilsevent == null || result.ilsevent == '' ? null : Number(result.ilsevent) ) != -1) ||
                     (override_params.overridable_events.indexOf( result.textcode ) != -1)
                 )
             ) {
@@ -501,7 +501,7 @@
                     if ( 
                         (result[i].ilsevent != 'undefined') && 
                         (
-                            (override_params.overridable_events.indexOf( result[i].ilsevent == null ? null : Number(result[i].ilsevent) ) != -1) ||
+                            (override_params.overridable_events.indexOf( result[i].ilsevent == null || result.ilsevent == '' ? null : Number(result[i].ilsevent) ) != -1) ||
                             (override_params.overridable_events.indexOf( result[i].textcode ) != -1) 
                         )
                     ) {

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/print.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/print.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/print.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -16,6 +16,10 @@
     var prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces['nsIPrefBranch']);
     var key = 'oils.printer.external.cmd.' + this.context;
     var has_key = prefs.prefHasUserValue(key);
+    if(!has_key && this.context != 'default') {
+        key = 'oils.printer.external.cmd.default';
+        has_key = prefs.prefHasUserValue(key);
+    }
     this.oils_printer_external_cmd = has_key ? prefs.getCharPref(key) : '';
 
     return this;

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/rbrowser.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/rbrowser.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/rbrowser.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -81,6 +81,10 @@
                     document.getElementById('browser_print').hidden = false;
                 }
 
+                if (xul_param('show_toolbar')) {
+                    document.getElementById('browser_toolbar').hidden = xul_param('show_toolbar');
+                }
+
                 if (xul_param('title')) {
                     try { document.title = xul_param('title'); } catch(E) {}
                     try { window.title = xul_param('title'); } catch(E) {}
@@ -124,7 +128,7 @@
     </popupset>
 
     <vbox flex="1">
-        <hbox>
+        <hbox id="browser_toolbar">
             <button id="back" command="cmd_back" disabled="true" hidden="true"/>
             <button id="reload" command="cmd_reload" disabled="false" hidden="false"/>
             <button id="forward" command="cmd_forward" disabled="true" hidden="true"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/widgets.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/widgets.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/widgets.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -27,6 +27,7 @@
     'set_text',
     'save_attributes',
     'load_attributes',
+    'find_descendants_by_name'
 ];
 util.widgets.EXPORT_TAGS    = { ':all' : util.widgets.EXPORT_OK };
 
@@ -449,4 +450,10 @@
 	e.setAttribute('properties', prop_class_string);
 }
 
+util.widgets.find_descendants_by_name = function(top_node,name) {
+    top_node = util.widgets.get(top_node);
+    if (!top_node) { return []; }
+    return top_node.getElementsByAttribute('name',name);
+}
+
 dump('exiting util/widgets.js\n');

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/window.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/window.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/content/util/window.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -17,18 +17,6 @@
     // list of documents for debugging.  BROKEN
     'doc_list' : [],    
 
-    // Windows need unique names.  This number helps.
-    'window_name_increment' :  function() {
-        JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
-        if (typeof data.window_name_increment == 'undefined') {
-            data.window_name_increment = 1;
-        } else {
-            data.window_name_increment++;
-        }
-        data.stash('window_name_increment');
-        return data.window_name_increment;
-    },
-
     // This number gets put into the title bar for Top Level menu interface windows
     'appshell_name_increment' : function() {
         JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
@@ -74,7 +62,7 @@
     'open' : function(url,title,features,my_xulG) {
         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         var key;
-        if (!title) title = 'anon' + this.window_name_increment();
+        if (!title) title = '_blank';
         if (!features) features = 'chrome';
         this.error.sdump('D_WIN', 'opening ' + url + ', ' + title + ', ' + features + ' from ' + this.win + '\n');
         var data;

Modified: branches/serials-integration/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties	2010-10-18 14:05:06 UTC (rev 18372)
@@ -115,6 +115,7 @@
 menu.cmd_browse_hold_pull_list.tab=On Shelf Pull List
 menu.cmd_local_admin.tab=Local Administration
 menu.cmd_open_vandelay.tab=MARC Import/Export
+menu.cmd_marc_batch_edit.tab=MARC Batch Edit
 menu.cmd_open_conify.tab=Server Settings
 menu.cmd_retrieve_last_patron.session.error=No patron visited yet this session.
 menu.cmd_retrieve_last_record.session.error=No record visited yet this session.
@@ -139,6 +140,7 @@
 menu.cmd_verify_credentials.tabname=Verify Credentials
 menu.close_all_tabs.error=Error closing all tabs
 menu.new_tab.tab=Tab
+menu.new_tab.max_tab_dialog=Sorry, we can't create any more tabs in this window.\nWould you like to create a new tab in another window?
 main.session_cookie.error=Error setting session cookie: %1$s
 menu.set_tab.error=pause for error
 menu.reset_network_stats=Reset network activity summary?
@@ -267,3 +269,13 @@
 printing.prompt_for_external_print_cmd=Enter external print command and parameters (use %receipt.txt% or %receipt.html% as the file containing the print data. Those values will be substituted with the proper path.):
 printing.print_strategy_saved=Print strategy (%1$s) for %2$s context saved to file system.
 text_editor.prompt_for_external_cmd=Enter external text editor command and parameters (use %letter.txt% as the file containing the text. This value will be substituted with the proper path.):
+menu.tab1.accesskey=1
+menu.tab2.accesskey=2
+menu.tab3.accesskey=3
+menu.tab4.accesskey=4
+menu.tab5.accesskey=5
+menu.tab6.accesskey=6
+menu.tab7.accesskey=7
+menu.tab8.accesskey=8
+menu.tab9.accesskey=9
+menu.tab10.accesskey=0

Copied: branches/serials-integration/Open-ILS/xul/staff_client/components/clh.js (from rev 18366, trunk/Open-ILS/xul/staff_client/components/clh.js)
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/components/clh.js	                        (rev 0)
+++ branches/serials-integration/Open-ILS/xul/staff_client/components/clh.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,221 @@
+const nsISupports           = Components.interfaces.nsISupports;
+const nsICategoryManager    = Components.interfaces.nsICategoryManager;
+const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
+const nsICommandLine        = Components.interfaces.nsICommandLine;
+const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler;
+const nsIFactory            = Components.interfaces.nsIFactory;
+const nsIModule             = Components.interfaces.nsIModule;
+const nsIWindowWatcher      = Components.interfaces.nsIWindowWatcher;
+
+
+const XUL_STANDALONE = "chrome://open_ils_staff_client/content/circ/offline.xul";
+const XUL_MAIN = "chrome://open_ils_staff_client/content/main/main.xul";
+const WINDOW_STANDALONE = "eg_offline"
+const WINDOW_MAIN = "eg_main"
+
+const clh_contractID = "@mozilla.org/commandlinehandler/general-startup;1?type=egcli";
+const clh_CID = Components.ID("{7e608198-7355-483a-a85a-20322e4ef91a}");
+// category names are sorted alphabetically. Typical command-line handlers use a
+// category that begins with the letter "m".
+const clh_category = "m-egcli";
+
+/**
+ * Utility functions
+ */
+
+/**
+ * Opens a chrome window.
+ * @param aChromeURISpec a string specifying the URI of the window to open.
+ * @param aArgument an argument to pass to the window (may be null)
+ */
+function findOrOpenWindow(aWindowType, aChromeURISpec, aName, aArgument)
+{
+  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+    getService(Components.interfaces.nsIWindowMediator);
+  var targetWindow = wm.getMostRecentWindow(aWindowType);
+  if (targetWindow != null) {
+      if(typeof targetWindow.new_tabs == 'function' && aArgument != null)
+      {
+          targetWindow.new_tabs(aArgument);
+      }
+      else {
+          targetwindow.focus;
+      }
+  }
+  else {
+    var params = null;
+    if (aArgument != null && aArgument.length != 0)
+    {
+        params = { "openTabs" : aArgument };
+        params.wrappedJSObject = params;
+    }
+    var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
+        getService(Components.interfaces.nsIWindowWatcher);
+    ww.openWindow(null, aChromeURISpec, aName,
+        "chrome,resizable,dialog=no", params);
+  }
+}
+ 
+/**
+ * The XPCOM component that implements nsICommandLineHandler.
+ * It also implements nsIFactory to serve as its own singleton factory.
+ */
+const myAppHandler = {
+  /* nsISupports */
+  QueryInterface : function clh_QI(iid)
+  {
+    if (iid.equals(nsICommandLineHandler) ||
+        iid.equals(nsIFactory) ||
+        iid.equals(nsISupports))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  /* nsICommandLineHandler */
+
+  handle : function clh_handle(cmdLine)
+  {
+    // Each of these options is used for opening a new tab, either on login or remote send.
+    // XULRunner does some sanitize to turn /ilsblah into -ilsblah, for example.
+    // In addition to the ones here, -ilslogin, -ilsoffline, and -ilsstandalone
+    // are defined below.
+
+    // With the exception of 'new', 'tab', and 'init', the value is checked for in urls in main.js.
+
+    // NOTE: The option itself should be all lowercase (we .toLowerCase below)
+    var options = {
+    '-ilscheckin' : 'XUL_CHECKIN',
+    '-ilscheckout' : 'XUL_PATRON_BARCODE_ENTRY',
+    '-ilsnew' : 'new', // 'new' is a special keyword for opening a new window
+    '-ilstab' : 'tab', // 'tab' is a special keyword for opening a new tab with the default content
+    '-ilsnew_default' : 'init', // 'init' is a special keyword for opening a new window with an initial default tab
+    };
+
+    var inParams = new Array();
+	var position = 0;
+	while (position < cmdLine.length) {
+		var arg = cmdLine.getArgument(position).toLowerCase();
+		if (options[arg] != undefined) {
+			inParams.push(options[arg]);
+			cmdLine.removeArguments(position,position);
+			continue;
+		}
+        if (arg == '-ilsurl' && cmdLine.length > position) {
+		  inParams.push(cmdLine.getArgument(position + 1));
+		  cmdLine.removeArguments(position, position + 1);
+		  continue;
+		}
+		position=position + 1;
+	}
+
+	if (cmdLine.handleFlag("ILSlogin", false) || inParams.length > 0) {
+	  findOrOpenWindow(WINDOW_MAIN, XUL_MAIN, '_blank', inParams);
+	  cmdLine.preventDefault = true;
+	}
+
+    if (cmdLine.handleFlag("ILSoffline", false) || cmdLine.handleFlag("ILSstandalone", false)) {
+   	  findOrOpenWindow(WINDOW_STANDALONE, XUL_STANDALONE, 'Offline', null);
+      cmdLine.preventDefault = true;
+   	}
+  },
+
+  // CHANGEME: change the help info as appropriate, but
+  // follow the guidelines in nsICommandLineHandler.idl
+  // specifically, flag descriptions should start at
+  // character 24, and lines should be wrapped at
+  // 72 characters with embedded newlines,
+  // and finally, the string should end with a newline
+  helpInfo : "  -ILScheckin          Open an Evergreen checkin tab\n" +
+             "  -ILScheckout         Open an Evergreen checkout tab\n" +
+             "  -ILSnew              Open a new Evergreen 'menu' window\n" +
+             "  -ILSnew_default      Open a new Evergreen 'menu' window,\n" +
+             "                       with a 'default' tab\n" +
+             "  -ILStab              Open a 'default' tab alone\n" +
+             "  -ILSurl <url>        Open the specified url in an Evergreen tab\n" +
+             "  The above six imply -ILSlogin\n" +
+             "  -ILSlogin            Open the Evergreen Login window\n" +
+             "  -ILSstandalone       Open the Evergreen Standalone interface\n" +
+             "  -ILSoffline          Alias for -ILSstandalone\n",
+
+  /* nsIFactory */
+
+  createInstance : function clh_CI(outer, iid)
+  {
+    if (outer != null)
+      throw Components.results.NS_ERROR_NO_AGGREGATION;
+
+    return this.QueryInterface(iid);
+  },
+
+  lockFactory : function clh_lock(lock)
+  {
+    /* no-op */
+  }
+};
+
+/**
+ * The XPCOM glue that implements nsIModule
+ */
+const myAppHandlerModule = {
+  /* nsISupports */
+  QueryInterface : function mod_QI(iid)
+  {
+    if (iid.equals(nsIModule) ||
+        iid.equals(nsISupports))
+      return this;
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  /* nsIModule */
+  getClassObject : function mod_gch(compMgr, cid, iid)
+  {
+    if (cid.equals(clh_CID))
+      return myAppHandler.QueryInterface(iid);
+
+    throw Components.results.NS_ERROR_NOT_REGISTERED;
+  },
+
+  registerSelf : function mod_regself(compMgr, fileSpec, location, type)
+  {
+    compMgr.QueryInterface(nsIComponentRegistrar);
+
+    compMgr.registerFactoryLocation(clh_CID,
+                                    "myAppHandler",
+                                    clh_contractID,
+                                    fileSpec,
+                                    location,
+                                    type);
+
+    var catMan = Components.classes["@mozilla.org/categorymanager;1"].
+      getService(nsICategoryManager);
+    catMan.addCategoryEntry("command-line-handler",
+                            clh_category,
+                            clh_contractID, true, true);
+  },
+
+  unregisterSelf : function mod_unreg(compMgr, location, type)
+  {
+    compMgr.QueryInterface(nsIComponentRegistrar);
+    compMgr.unregisterFactoryLocation(clh_CID, location);
+
+    var catMan = Components.classes["@mozilla.org/categorymanager;1"].
+      getService(nsICategoryManager);
+    catMan.deleteCategoryEntry("command-line-handler", clh_category);
+  },
+
+  canUnload : function (compMgr)
+  {
+    return true;
+  }
+};
+
+/* The NSGetModule function is the magic entry point that XPCOM uses to find what XPCOM objects
+ * this component provides
+ */
+function NSGetModule(comMgr, fileSpec)
+{
+  return myAppHandlerModule;
+}
+

Modified: branches/serials-integration/Open-ILS/xul/staff_client/external/dojo_template.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/external/dojo_template.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/external/dojo_template.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="main_test_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/external/template.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/external/template.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/external/template.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="example_template_win" 
-    onload="try { my_init(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/admin/do_not_auto_attempt_print_setting.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/admin/do_not_auto_attempt_print_setting.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/admin/do_not_auto_attempt_print_setting.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="main_test_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/admin/font_settings.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/admin/font_settings.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/admin/font_settings.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="font_settings_win" 
-    onload="try { my_init(); } catch(E) { alert(E); }" style="background: white;"
+    onload="try { my_init(); persist_helper(); } catch(E) { alert(E); }" style="background: white;"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/admin/index.xhtml
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/admin/index.xhtml	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/admin/index.xhtml	2010-10-18 14:05:06 UTC (rev 18372)
@@ -24,10 +24,7 @@
             function getBuildId() { return location.href.match(/\/xul\/(.+?)\/server\//)[1]; }
 
             function my_init() {
-                try {
-                    dojo.require("dojo.cookie");
-                    window.xulG.auth = {"session": {"key": dojo.cookie("ses")}};
-                } catch(E) { /* XXX ignorable except for booking links */ }
+                ;
             }
         </script>
         <style type='text/css'>
@@ -50,7 +47,6 @@
                         <th width='25%'>&staff.server.admin.index.workstation_configuration;</th>
                         <th width='25%'>&staff.server.admin.index.library_configuration;</th>
                         <th width='25%'>&staff.server.admin.index.maintenance_reports;</th>
-                        <th width='25%'>&staff.server.admin.index.booking;</th>
                     </tr>
                 </thead>
                 <tbody>
@@ -80,6 +76,9 @@
                             </div>
                         </td><td>
                             <div style='padding: 8px;'>
+                                <a href='javascript:window.xulG.new_tab(window.xulG.url_prefix("/opac/extras/circ/alt_holds_print.html").replace("http","https") + "?do=shelf_expired_holds&amp;chunk_size=25",{"tab_name":"&staff.server.admin.index.expired_holds_shelf;"},{});'>&staff.server.admin.index.expired_holds_shelf;</a>
+                            </div>
+                            <div style='padding: 8px;'>
                                 <a href='javascript:window.xulG.new_tab("/xul/server/patron/holds.xul",{"tab_name":"&staff.server.admin.index.hold_pull_list;"},{});'>&staff.server.admin.index.hold_pull_list;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
                             </div>
                             <div style='padding: 8px;'>
@@ -94,22 +93,6 @@
                             <div style='padding: 8px;'>
                                 <a href='javascript:window.xulG.new_tab("/xul/server/admin/transit_list.xul",{"tab_name":"&staff.server.admin.index.transits;"},{});'>&staff.server.admin.index.transit_list;</a>
                             </div>
-                        </td><td>
-                            <div style='padding: 8px;'>
-                                <a href='javascript:window.xulG.new_tab("/eg/booking/reservation",{"tab_name":"&staff.server.admin.index.booking.reservation;","browser":false},window.xulG);'>&staff.server.admin.index.booking.reservation;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
-                            </div>
-                            <div style='padding: 8px;'>
-                                <a href='javascript:window.xulG.new_tab("/eg/booking/pull_list",{"tab_name":"&staff.server.admin.index.booking.pull_list;","browser":false},window.xulG);'>&staff.server.admin.index.booking.pull_list;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
-                            </div>
-                            <div style='padding: 8px;'>
-                                <a href='javascript:window.xulG.new_tab("/eg/booking/capture",{"tab_name":"&staff.server.admin.index.booking.capture;","browser":false},window.xulG);'>&staff.server.admin.index.booking.capture;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
-                            </div>
-                            <div style='padding: 8px;'>
-                                <a href='javascript:window.xulG.new_tab("/eg/booking/pickup",{"tab_name":"&staff.server.admin.index.booking.pickup;","browser":false},window.xulG);'>&staff.server.admin.index.booking.pickup;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
-                            </div>
-                            <div style='padding: 8px;'>
-                                <a href='javascript:window.xulG.new_tab("/eg/booking/return",{"tab_name":"&staff.server.admin.index.booking.return;","browser":false},window.xulG);'>&staff.server.admin.index.booking.return;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
-                            </div>
                         </td>
                     </tr>
                 </tbody>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/admin/offline_manage_xacts.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/admin/offline_manage_xacts.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/admin/offline_manage_xacts.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,7 +19,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="admin_offline_manage_xacts_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
@@ -55,7 +55,7 @@
 
     <vbox id="admin_offline_manage_xacts_main" flex="1">
 
-        <groupbox flex="1">
+        <groupbox flex="1" id="before_splitter" oils_persist="height">
             <caption label="&staff.server.admin.offline.xacts.caption;"/>
             <hbox>
                 <button id="refresh" label="&common.refresh;" accesskey="&staff.server.admin.offline.xacts.refresh.accesskey;"/>
@@ -66,8 +66,8 @@
             </hbox>
             <tree id="session_tree" enableColumnDrag="true" seltype="single" flex="1"/>
         </groupbox>
-            <splitter><grippy/></splitter>
-        <deck flex="1" id="deck">
+        <splitter id="splitter" oils_persist="state" oils_persist_peers="before_splitter deck"><grippy/></splitter>
+        <deck flex="1" id="deck" oils_persist="height">
             <label value=" "/>
             <groupbox flex="1">
                 <caption id="status_caption" label="&staff.server.admin.offline.xacts.status.label;"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/admin/transit_list.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/admin/transit_list.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/admin/transit_list.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="admin_transit_list_win" active="true" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/admin/work_log.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/admin/work_log.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/admin/work_log.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -33,7 +33,7 @@
     <script type="text/javascript" src="work_log.js"/>
 
     <vbox flex="1">
-        <vbox flex="1">
+        <vbox flex="1" id="before_splitter" oils_persist="height">
             <hbox>
                 <textbox id="desire_number_of_work_log_entries" type="number" oils_persist="value" />
                 <label value="&staff.admin.work_log.list1.header;" class="header1"/>
@@ -44,8 +44,8 @@
             </hbox>
             <tree id="work_action_log" flex="1" enableColumnDrag="true" context="work_log_actions"/>
         </vbox>
-        <splitter><grippy/></splitter>
-        <vbox flex="1">
+        <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy/></splitter>
+        <vbox flex="1" id="after_splitter" oils_persist="height">
             <hbox>
                 <textbox id="desire_number_of_patron_log_entries" type="number" oils_persist="value" />
                 <label value="&staff.admin.work_log.list2.header;" class="header1"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -22,7 +22,7 @@
 <?xul-overlay href="/xul/server/cat/bib_brief_overlay.xul"?>
 
 <window id="cat_bib_brief_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Copied: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_overlay_vertical.xul (from rev 18366, trunk/Open-ILS/xul/staff_client/server/cat/bib_brief_overlay_vertical.xul)
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_overlay_vertical.xul	                        (rev 0)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_overlay_vertical.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!DOCTYPE overlay PUBLIC "" ""[
+    <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+<overlay id="bib_brief_overlay" 
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+        <script type="text/javascript" src="/xul/server/cat/bib_brief_overlay.js"/>
+
+        <grid id="bib_brief_grid" flex="0">
+            <columns>
+                <column />
+                <column />
+            </columns>
+            <rows id="bib_brief_grid_rows">
+                <row id="bib_brief_grid_row1" position="1">
+                    <label value="&staff.cat.bib_brief.title.label;" accesskey="&staff.cat.bib_brief.title.accesskey;" control="title" class="emphasis"/>
+                    <textbox id="title" name="title" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label value="&staff.cat.bib_brief.author.label;" accesskey="&staff.cat.bib_brief.author.accesskey;" control="author" class="emphasis"/>
+                    <textbox id="author" name="author" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row position="2">
+                    <label value="&staff.cat.bib_brief.edition.label;" accesskey="&staff.cat.bib_brief.edition.accesskey;" control="edition" class="emphasis"/>
+                    <textbox id="edition" name="edition" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label value="&staff.cat.bib_brief.pub_date.label;" accesskey="&staff.cat.bib_brief.pub_date.accesskey;" control="pubdate" class="emphasis"/>
+                    <textbox id="pubdate" name="pubdate" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label id="bib_call_number_label" value="&staff.cat.bib_brief.call_number.label;" accesskey="&staff.cat.bib_brief.call_number.accesskey;" control="bib_call_number" class="emphasis"/>
+                    <textbox id="bib_call_number" name="bib_call_number" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row id="bib_brief_grid_row3" position="3">
+                    <label value="&staff.cat.bib_brief.title_control_number.label;" accesskey="&staff.cat.bib_brief.title_control_number.accesskey;" control="tcn" class="emphasis"/>
+                    <textbox id="tcn" name="tcn" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label value="&staff.cat.bib_brief.biblio_record_entry_id.label;" accesskey="&staff.cat.bib_brief.biblio_record_entry_id.accesskey;" control="mvr_doc_id" class="emphasis"/>
+                    <textbox id="mvr_doc_id" name="mvr_doc_id" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label value="&staff.cat.bib_brief.biblio_record_entry_owner.label;" accesskey="&staff.cat.bib_brief.biblio_record_entry_owner.accesskey;" control="owner" class="emphasis"/>
+                    <textbox id="owner" name="owner" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label value="&staff.cat.bib_brief.created_by.label;" accesskey="&staff.cat.bib_brief.created_by.accesskey;" control="creator" class="emphasis"/>
+                    <textbox id="creator" name="creator" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label value="&staff.cat.bib_brief.last_edited_by.label;" accesskey="&staff.cat.bib_brief.last_edited_by.accesskey;" control="editor" class="emphasis"/>
+                    <textbox id="editor" name="editor" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+                <row>
+                    <label value="&staff.cat.bib_brief.last_edited_on.label;" accesskey="&staff.cat.bib_brief.last_edited_on.accesskey;" control="edit_date" class="emphasis"/>
+                    <textbox id="edit_date" name="edit_date" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+                </row>
+            </rows>
+        </grid>
+</overlay>

Copied: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_vertical.xul (from rev 18366, trunk/Open-ILS/xul/staff_client/server/cat/bib_brief_vertical.xul)
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_vertical.xul	                        (rev 0)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bib_brief_vertical.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Application: Evergreen Staff Client -->
+<!-- Screen: Brief Bib Display -->
+<!--
+vim: noet:sw=4:ts=4:
+-->
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- STYLESHEETS -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- LOCALIZATION -->
+<!DOCTYPE window PUBLIC "" ""[
+    <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- OVERLAYS -->
+<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
+<?xul-overlay href="/xul/server/cat/bib_brief_overlay_vertical.xul"?>
+
+<window id="cat_bib_brief_win" 
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+    <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+    <!-- BEHAVIOR -->
+        <script type="text/javascript">
+        var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true; var g = {};
+    </script>
+        <scripts id="openils_util_scripts"/>
+
+    <script type="text/javascript" src="/xul/server/main/JSAN.js"/>
+    <script type="text/javascript" src="/xul/server/cat/bib_brief.js"/>
+
+    <messagecatalog id="catStrings" src="/xul/server/locale/<!--#echo var='locale'-->/cat.properties"/>
+    <messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties"/>
+
+    <groupbox id="groupbox" flex="1">
+        <caption id="caption"><label value="&staff.cat.bib_brief.record_summary;"/>(<label value="&staff.cat.bib_brief.view_marc;" class="click_link" onclick="view_marc();"/>)</caption>
+        <grid id="bib_brief_grid" />
+    </groupbox>
+
+</window>
+

Copied: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.js (from rev 18366, trunk/Open-ILS/xul/staff_client/server/cat/bibs_abreast.js)
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.js	                        (rev 0)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,207 @@
+var error;
+var network;
+var record_ids;
+var lead_record;
+
+function my_init() {
+    try {
+        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+        if (typeof JSAN == 'undefined') { throw( "The JSAN library object is missing."); }
+        JSAN.errorLevel = "die"; // none, warn, or die
+        JSAN.addRepository('/xul/server/');
+        JSAN.use('util.error'); error = new util.error();
+        error.sdump('D_TRACE','my_init() for bibs_abreast.xul');
+        JSAN.use('util.functional');
+        JSAN.use('util.widgets');
+        JSAN.use('cat.util');
+        JSAN.use('util.network');
+
+        network = new util.network();
+
+        record_ids = xul_param('record_ids') || [];
+        record_ids = util.functional.unique_list_values( record_ids );
+
+        // Merge UI 
+        if (xul_param('merge')) {
+            var x = document.getElementById('merge_bar');
+            x.hidden = false;
+            var y = document.getElementById('merge_button');
+            y.addEventListener('command', merge_records, false);
+            var z = document.getElementById('cancel_button');
+            z.addEventListener('command', function() {
+                x.hidden = true;
+                y.disabled = true;
+                var merge_bars = util.widgets.find_descendants_by_name(document,'merge_bar');
+                for (var i = 0; i < merge_bars.length; i++) { merge_bars[i].hidden = true; }
+            }, false);
+        }
+
+        // Display the records
+        for (var i = 0; i < record_ids.length; i++) {
+            render_bib(record_ids[i]);
+        }
+
+        /*if (typeof window.xulG == 'object' && typeof window.xulG.set_tab_name == 'function') {
+            try { window.xulG.set_tab_name('Test'); } catch(E) { alert(E); }
+        }*/
+
+    } catch(E) {
+        try { error.standard_unexpected_error_alert('main/test.xul',E); } catch(F) { alert(E); }
+    }
+}
+
+function render_bib(record_id) {
+    var main = document.getElementById('main');
+    var template = main.firstChild;
+    var new_node = template.cloneNode(true);
+    main.appendChild(new_node);
+    new_node.hidden = false;
+
+    var splitter_template = template.nextSibling;
+    var splitter = splitter_template.cloneNode(true);
+    main.appendChild(splitter);
+    splitter.hidden = false;
+
+    render_bib_brief(new_node,record_id);
+
+    var xul_deck = util.widgets.find_descendants_by_name(new_node,'bib_deck')[0];
+    var deck = new util.deck(xul_deck);
+
+    // merge UI
+    if (xul_param('merge')) {
+        var merge_bar = util.widgets.find_descendants_by_name(new_node,'merge_bar')[0];
+        merge_bar.hidden = false;
+        var lead_button = util.widgets.find_descendants_by_name(new_node,'lead_button')[0];
+        lead_button.addEventListener('click', function() {
+            lead_record = record_id;
+            dump('record_id = ' + record_id + '\n');
+            document.getElementById('merge_button').disabled = false;
+        }, false);
+    }
+
+    // remove_me button
+    var remove_me = util.widgets.find_descendants_by_name(new_node,'remove_me')[0];
+    remove_me.addEventListener('command', function() {
+        if (lead_record == record_id) {
+            lead_record = undefined;
+            document.getElementById('merge_button').disabled = true;
+        }
+        record_ids = util.functional.filter_list( record_ids, function(o) { return o != record_id; } );
+        main.removeChild(new_node);
+        main.removeChild(splitter);
+        if (main.childNodes.length == 4) {
+            document.getElementById('merge_bar').hidden = true;
+            document.getElementById('merge_button').disabled = true;
+            var merge_bars = util.widgets.find_descendants_by_name(document,'merge_bar');
+            for (var i = 0; i < merge_bars.length; i++) { merge_bars[i].hidden = true; }
+        }
+        if (main.childNodes.length == 2) { xulG.close_tab(); }
+    }, false);
+
+    // radio buttons
+    var view_bib = util.widgets.find_descendants_by_name(new_node,'view_bib')[0];
+    var edit_bib = util.widgets.find_descendants_by_name(new_node,'edit_bib')[0];
+    var holdings = util.widgets.find_descendants_by_name(new_node,'holdings')[0];
+
+    view_bib.addEventListener('command', function() {
+        set_view_pane(deck,record_id);
+    }, false); 
+
+    edit_bib.addEventListener('command', function() {
+        set_edit_pane(deck,record_id);
+    }, false); 
+
+    holdings.addEventListener('command', function() {
+        set_item_pane(deck,record_id);
+    }, false); 
+
+    set_view_pane(deck,record_id);
+
+}
+
+function render_bib_brief(new_node,record_id) {
+    // iframe
+    var bib_brief = util.widgets.find_descendants_by_name(new_node,'bib_brief')[0];
+    bib_brief.setAttribute('src', urls.XUL_BIB_BRIEF_VERTICAL);
+    get_contentWindow(bib_brief).xulG = { 'docid' : record_id };
+}
+
+function set_view_pane(deck,record_id) {
+    deck.set_iframe( urls.XUL_MARC_VIEW, {}, { 'docid' : record_id } );
+}
+
+function set_item_pane(deck,record_id) {
+    var my_xulG = { 'docid' : record_id }; for (var i in xulG) { my_xulG[i] = xulG[i]; }
+    deck.set_iframe( urls.XUL_COPY_VOLUME_BROWSE, {}, my_xulG );
+}
+
+function set_edit_pane(deck,record_id) {
+    var my_xulG = {
+        'record' : { 'url' : '/opac/extras/supercat/retrieve/marcxml/record/' + record_id, "id": record_id, "rtype": "bre" },
+        'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
+            try {
+                return cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
+            } catch(E) {
+                alert('Error in bibs_abreast.js, set_edit_pane, fast_item_add: ' + E);
+            }
+        },
+        'save' : {
+            'label' : document.getElementById('offlineStrings').getString('cat.save_record'),
+            'func' : function (new_marcxml) {
+                try {
+                    var r = network.simple_request('MARC_XML_RECORD_UPDATE', [ ses(), record_id, new_marcxml ]);
+                    if (typeof r.ilsevent != 'undefined') {
+                        throw(r);
+                    } else {
+                        return {
+                            'id' : r.id(),
+                            'oncomplete' : function() {}
+                        };
+                    }
+                } catch(E) {
+                    alert('Error in bibs_abreast.js, set_edit_pane, save: ' + E);
+                }
+            }
+        }
+    };
+    for (var i in xulG) { my_xulG[i] = xulG[i]; }
+    deck.set_iframe( urls.XUL_MARC_EDIT, {}, my_xulG );
+}
+
+function merge_records() {
+    try {
+        var robj = network.simple_request('MERGE_RECORDS',
+            [
+                ses(),
+                lead_record,
+                util.functional.filter_list( record_ids,
+                    function(o) {
+                        return o != lead_record;
+                    }
+                )
+            ]
+        );
+        if (typeof robj.ilsevent != 'undefined') {
+            switch(robj.ilsevent) {
+                case 5000 /* PERM_FAILURE */: break;
+                default: throw(robj);
+            }
+        }
+        if (typeof xulG.on_merge == 'function') {
+            xulG.on_merge(robj);
+        }
+        var opac_url = xulG.url_prefix( urls.opac_rdetail ) + '?r=' + lead_record;
+        var content_params = {
+            'session' : ses(),
+            'authtime' : ses('authtime'),
+            'opac_url' : opac_url,
+        };
+        xulG.set_tab(
+            xulG.url_prefix(urls.XUL_OPAC_WRAPPER),
+            {'tab_name':'Retrieving title...'},
+            content_params
+        );
+    } catch(E) {
+        alert('Error in bibs_abreast.js, merge_records(): ' + E);
+    }
+}

Copied: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.xul (from rev 18366, trunk/Open-ILS/xul/staff_client/server/cat/bibs_abreast.xul)
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.xul	                        (rev 0)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/bibs_abreast.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!-- Application: Evergreen Staff Client -->
+<!-- Screen: Example Template for remote xul -->
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- STYLESHEETS -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- LOCALIZATION -->
+<!DOCTYPE window PUBLIC "" ""[
+    <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- OVERLAYS -->
+<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
+
+<window id="main_bibs_abreast" 
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+    <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+    <!-- BEHAVIOR -->
+    <script type="text/javascript">
+        var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true;
+    </script>
+    <scripts id="openils_util_scripts"/>
+
+    <script type="text/javascript" src="/xul/server/main/JSAN.js"/>
+    <script type="text/javascript" src="bibs_abreast.js"/>
+
+    <hbox id="merge_bar" hidden="true">
+        <description>&staff.cat.record_buckets.merge_records.merge_lead;</description>
+        <button id="merge_button" disabled="true"
+            label="&staff.cat.record_buckets.merge_records.button.label;"
+            accesskey="&staff.cat.record_buckets.merge_records.button.accesskey;"
+        />
+        <button id="cancel_button" 
+            label="&staff.cat.record_buckets.merge_records.cancel_button.label;"
+            accesskey="&staff.cat.record_buckets.merge_records.cancel_button.accesskey;"
+        />
+    </hbox>
+    <hbox id="main" class="my_overflow" flex="1">
+        <vbox name="template" hidden="true" flex="1">
+            <hbox name="merge_bar" hidden="true">
+                <input name="lead_button" type="radio" xmlns="http://www.w3.org/1999/xhtml"></input>
+                &staff.cat.record_buckets.merge_records.lead;
+            </hbox>
+            <iframe name="bib_brief" flex="1" />
+            <hbox>
+                <radiogroup flex="0" orient="horizontal">
+                    <radio name="view_bib" label="View Bib" />
+                    <radio name="edit_bib" label="Edit Bib" />
+                    <radio name="holdings" label="Holdings" />
+                </radiogroup>
+                <spacer flex="1"/>
+                <button name="remove_me"
+                    image="/xul/server/skin/media/images/icon_delete.gif"
+                    label="&staff.cat.record_buckets.merge_records.remove_from_consideration;" />
+            </hbox>
+            <deck name="bib_deck" flex="3" />
+        </vbox>
+        <splitter name="template2" hidden="true"><grippy /></splitter>
+    </hbox>
+
+</window>
+

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1251,7 +1251,6 @@
         var obj = this;
         try {
             var org = obj.data.hash.aou[ org_id ];
-            if (obj.data.hash.aout[ org.ou_type() ].depth() == 0 && ! get_bool( obj.data.hash.aout[ org.ou_type() ].can_have_vols() ) ) return;
             obj.funcs.push( function() { 
                 document.getElementById('cmd_refresh_list').setAttribute('disabled','true'); 
                 document.getElementById('cmd_show_libs_with_copies').setAttribute('disabled','true'); 
@@ -1376,10 +1375,8 @@
             }
 
             if (document.getElementById('show_acns').checked) {
-                if (! ( obj.data.hash.aout[ org.ou_type() ].depth() == 0 && ! get_bool( obj.data.hash.aout[ org.ou_type() ].can_have_vols() ) )) {
-                    node.setAttribute('open','true');
-                    obj.funcs.push( function() { obj.on_select_org( org.id() ); } );
-                }
+                node.setAttribute('open','true');
+                obj.funcs.push( function() { obj.on_select_org( org.id() ); } );
             }
 
         } catch(E) {

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_browser.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -22,7 +22,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="cat_copy_browser" active="true"
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_buckets_overlay.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_buckets_overlay.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_buckets_overlay.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -14,14 +14,14 @@
 </box>
 
 <vbox id="cmvb1" flex="1">
-    <groupbox flex="1">
+    <groupbox flex="1" id="before_splitter" oils_persist="height">
         <caption label="&staff.cat.copy_buckets_overlay.pending_copies;" />
         <hbox id="pending_buckets_top_ui" />
         <tree id="pending_copies_list" flex="1" enableColumnDrag="true"/>
         <hbox id="pending_buckets_bottom_ui" />
     </groupbox>
-    <splitter><grippy /></splitter>
-    <groupbox flex="2">
+    <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy /></splitter>
+    <groupbox flex="2" id="after_splitter" oils_persist="height">
         <caption label="&staff.cat.copy_buckets_overlay.bucket_view;" />
         <hbox id="copy_buckets_top_ui" />
         <hbox id="info_box"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_editor.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_editor.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_editor.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -56,27 +56,27 @@
         </hbox>
 
         <hbox flex="1" style="overflow: scroll">
-            <vbox flex="1">
+            <vbox flex="1" id="before_splitter1" oils_persist="width">
                 <label value="&staff.cat.copy_editor.identification.label;" style="font-weight: bold; font-size: large"/>
                 <vbox id="left_pane" flex="1"/>
             </vbox>
-            <splitter><grippy /></splitter>
-            <vbox flex="1">
+            <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1"><grippy /></splitter>
+            <vbox flex="1" id="after_splitter1" oils_persist="width">
                 <button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.location.label;" accesskey="&staff.cat.copy_editor.identification.location.accesskey;" oncommand="document.getElementById('right_pane').firstChild.firstChild.focus();"/>
                 <vbox id="right_pane" flex="1"/>
             </vbox>
-            <splitter><grippy /></splitter>
-            <vbox flex="1">
+            <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_splitter1 after_splitter2"><grippy /></splitter>
+            <vbox flex="1" id="after_splitter2" oils_persist="width">
                 <button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.circulation.label;" accesskey="&staff.cat.copy_editor.identification.circulation.accesskey;" oncommand="document.getElementById('right_pane2').firstChild.firstChild.focus();"/>
                 <vbox id="right_pane2" flex="1"/>
             </vbox>
-            <splitter><grippy /></splitter>
-            <vbox flex="1">
+            <splitter id="splitter3" oils_persist="state hidden" oils_persist_peers="after_splitter2 after_splitter3"><grippy /></splitter>
+            <vbox flex="1" id="after_splitter3" oils_persist="width">
                 <button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.miscellaneous.label;" accesskey="&staff.cat.copy_editor.identification.miscellaneous.accesskey;" oncommand="document.getElementById('right_pane3').firstChild.firstChild.focus();"/>
                 <vbox id="right_pane3" flex="1"/>
             </vbox>
-            <splitter><grippy /></splitter>
-            <vbox flex="1">
+            <splitter id="splitter4" oils_persist="state hidden" oils_persist_peers="after_splitter3 after_splitter4"><grippy /></splitter>
+            <vbox flex="1" id="after_splitter4" oils_persist="width">
                 <button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.statistics.label;" accesskey="&staff.cat.copy_editor.identification.statistics.accesskey;" oncommand="document.getElementById('right_pane4').firstChild.firstChild.focus();"/>
                 <menu label="&staff.cat.copy_editor.stat_cat_lib_filter_menu.label;" id="stat_cat_lib_filter_menu">
                     <menupopup />

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_notes.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_notes.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/copy_notes.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -22,8 +22,9 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="copy_notes_win" width="700" height="550"
+    oils_persist="height width sizemode"
     title="&staff.cat.copy_editor.copy_notes.label;"
-    onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_new.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_new.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_new.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -21,7 +21,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="example_template_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_view.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_view.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marc_view.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -21,7 +21,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="cat_marc_view_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -91,15 +91,22 @@
     }
 }
 
-function swap_editors () {
+function set_flat_editor (useFlatText) {
 
     dojo.require('MARC.Record');
 
     var xe = $('xul-editor');
     var te = $('text-editor');
 
-    te.hidden = te.hidden ? false : true;
-    xe.hidden = xe.hidden ? false : true;
+    if (useFlatText) {
+        if (xe.hidden) { return; }
+        te.hidden = false;
+        xe.hidden = true;
+    } else {
+        if (te.hidden) { return; }
+        te.hidden = true;
+        xe.hidden = false;
+    }
 
     if (te.hidden) {
         // get the marcxml from the text box
@@ -168,7 +175,7 @@
 
         document.getElementById('save-button').setAttribute('label', window.xulG.save.label);
         document.getElementById('save-button').setAttribute('oncommand',
-            'if ($("xul-editor").hidden) swap_editors(); ' +
+            'if ($("xul-editor").hidden) set_flat_editor(false); ' +
             'mangle_005(); ' + 
             'var xml_string = xml_escape_unicode( xml_record.toXMLString() ); ' + 
             'save_attempt( xml_string ); ' +
@@ -2326,7 +2333,7 @@
     ;
 
     // would be good to carve this out into a separate function
-    dojo.xhrGet({"url":url, "handleAs":"xml", "load": function(records) {
+    dojo.xhrGet({"url":url, "sync": true, "handleAs":"xml", "load": function(records) {
         var create_menu = createMenu({ label: $('catStrings').getString('staff.cat.marcedit.create_authority.label')});
 
         var cm_popup = create_menu.appendChild(

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/marcedit.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -24,6 +24,7 @@
     <caption label="&staff.cat.marcedit.options.label;"/>
     <hbox flex="1">
         <checkbox oils_persist="checked" accesskey="&staff.cat.marcedit.stackSubfields.accesskey;" label="&staff.cat.marcedit.stackSubfields.label;" oncommand="stackSubfields(this);" checked="false" id="stackSubfields"/>
+        <checkbox oils_persist="checked" accesskey="&staff.cat.marcedit.flatTextEditor.accesskey;" label="&staff.cat.marcedit.flatTextEditor.label;" oncommand="set_flat_editor(this.checked);" checked="false" id="swapEditor_checkbox"/>
         <checkbox oils_persist="checked" accesskey="&staff.cat.marcedit.fastItemAdd.accesskey;" label="&staff.cat.marcedit.fastItemAdd.label;" oncommand="fastItemAdd_toggle(this);" checked="false" id="fastItemAdd_checkbox"/>
         <hbox id="fastItemAdd_textboxes">
             <label control="fastItemAdd_callnumber" accesskey="&staff.cat.marcedit.fastItemAdd_callnumber.accesskey;" value="&staff.cat.marcedit.fastItemAdd_callnumber.label;" />
@@ -33,7 +34,6 @@
         </hbox>
         <button label="&staff.cat.marcedit.validate.label;" accesskey="&staff.cat.marcedit.validate.accesskey;" oncommand="validateAuthority(this);"/>
         <button id="save-button" accesskey="&staff.cat.marcedit.save-button.accesskey;"/>
-        <button label="&staff.cat.marcedit.swapEditor.label;" oncommand="swap_editors()"/>
         <button label="&staff.cat.marcedit.help.label;" accesskey="&staff.cat.marcedit.help.accesskey;"
             oncommand="alert(
                 $('catStrings').getString('staff.cat.marcedit.help.add_row') + '\n' +

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -342,6 +342,7 @@
                                     obj.controller.view.cmd_delete_records.setAttribute('disabled','true');
                                     obj.controller.view.cmd_sel_opac.setAttribute('disabled','true');
                                     obj.controller.view.cmd_transfer_title_holds.setAttribute('disabled','true');
+                                    obj.controller.view.cmd_marc_batch_edit.setAttribute('disabled','true');
                                     obj.controller.view.record_buckets_list_actions.disabled = true;
                                     var bucket = obj.network.simple_request(
                                         'BUCKET_FLESH',
@@ -363,6 +364,7 @@
                                         obj.controller.view.cmd_delete_records.setAttribute('disabled','false');
                                         obj.controller.view.cmd_sel_opac.setAttribute('disabled','false');
                                         obj.controller.view.cmd_transfer_title_holds.setAttribute('disabled','false');
+                                        obj.controller.view.cmd_marc_batch_edit.setAttribute('disabled','false');
                                         obj.controller.view.record_buckets_list_actions.disabled = false;
 
                                         var x = document.getElementById('info_box');
@@ -553,6 +555,7 @@
                                 obj.controller.view.cmd_delete_records.setAttribute('disabled','true');
                                 obj.controller.view.cmd_sel_opac.setAttribute('disabled','true');
                                 obj.controller.view.cmd_transfer_title_holds.setAttribute('disabled','true');
+                                obj.controller.view.cmd_marc_batch_edit.setAttribute('disabled','true');
                                 obj.controller.view.record_buckets_list_actions.disabled = true;
                                 obj.controller.render('record_buckets_menulist_placeholder');
                                 setTimeout(
@@ -653,75 +656,24 @@
                                     }
                                 );
 
-                                netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
-                                var top_xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" >';
-                                top_xml += '<description>' + $("catStrings").getString('staff.cat.record_buckets.merge_records.merge_lead') + '</description>';
-                                top_xml += '<hbox>';
-                                top_xml += '<button id="lead" disabled="true" label="'
-                                        + $("catStrings").getString('staff.cat.record_buckets.merge_records.button.label') + '" name="fancy_submit"/>';
-                                top_xml += '<button label="' + $("catStrings").getString('staff.cat.record_buckets.merge_records.cancel_button.label') +'" accesskey="'
-                                        + $("catStrings").getString('staff.cat.record_buckets.merge_records.cancel_button.accesskey') +'" name="fancy_cancel"/></hbox></vbox>';
-
-                                var xml = '<form xmlns="http://www.w3.org/1999/xhtml">';
-                                xml += '<table><tr valign="top">';
-                                for (var i = 0; i < record_ids.length; i++) {
-                                    xml += '<td><input value="' + $("catStrings").getString('staff.cat.record_buckets.merge_records.lead')
-                                    xml += '" id="record_' + record_ids[i] + '" type="radio" name="lead"';
-                                    xml += ' onclick="' + "try { var x = $('lead'); x.setAttribute('value',";
-                                    xml += record_ids[i] + '); x.disabled = false; } catch(E) { alert(E); }">';
-                                    xml += '</input>' + $("catStrings").getFormattedString('staff.cat.record_buckets.merge_records.lead_record_number',[record_ids[i]]) + '</td>';
-                                }
-                                xml += '</tr><tr valign="top">';
-                                for (var i = 0; i < record_ids.length; i++) {
-                                    xml += '<td nowrap="nowrap"><iframe src="' + urls.XUL_BIB_BRIEF; 
-                                    xml += '?docid=' + record_ids[i] + '"/></td>';
-                                }
-                                xml += '</tr><tr valign="top">';
-                                for (var i = 0; i < record_ids.length; i++) {
-                                    xml += '<td nowrap="nowrap"><iframe style="min-height: 1000px; min-width: 300px;" flex="1" src="' + urls.XUL_MARC_VIEW + '?docid=' + record_ids[i] + ' "/></td>';
-                                }
-                                xml += '</tr></table></form>';
-                                //obj.data.temp_merge_top = top_xml; obj.data.stash('temp_merge_top');
-                                //obj.data.temp_merge_mid = xml; obj.data.stash('temp_merge_mid');
-                                JSAN.use('util.window'); var win = new util.window();
-                                var fancy_prompt_data = win.open(
-                                    urls.XUL_FANCY_PROMPT,
-                                    //+ '?xml_in_stash=temp_merge_mid'
-                                    //+ '&top_xml_in_stash=temp_merge_top'
-                                    //+ '&title=' + window.escape('Record Merging'),
-                                    'fancy_prompt', 'chrome,resizable,modal,width=700,height=500',
-                                    {
-                                        'top_xml' : top_xml, 'xml' : xml, 'title' : $("catStrings").getString('staff.cat.record_buckets.merge_records.fancy_prompt_title')
+                                xulG.new_tab(
+                                    '/xul/server/cat/bibs_abreast.xul',{
+                                        'tab_name' : $("catStrings").getString('staff.cat.record_buckets.merge_records.fancy_prompt_title')
+                                    },{
+                                        'merge' : true,
+                                        'on_merge' : function() {
+                                            obj.render_pending_records(); // FIXME -- need a generic refresh for lists
+                                            setTimeout(
+                                                function() {
+                                                    JSAN.use('util.widgets'); 
+                                                    util.widgets.dispatch('change_bucket',obj.controller.view.bucket_menulist);
+                                                }, 0
+                                            );
+                                        },
+                                        'record_ids':record_ids
                                     }
                                 );
-                                //obj.data.stash_retrieve();
 
-                                if (typeof fancy_prompt_data.fancy_status == 'undefined' || fancy_prompt_data.fancy_status == 'incomplete') {
-                                    alert($("catStrings").getString('staff.cat.record_buckets.merge_records.fancy_prompt.alert'));
-                                    return;
-                                }
-                                var robj = obj.network.simple_request('MERGE_RECORDS', 
-                                    [ 
-                                        ses(), 
-                                        fancy_prompt_data.lead, 
-                                        util.functional.filter_list( record_ids,
-                                            function(o) {
-                                                return o != fancy_prompt_data.lead;
-                                            }
-                                        )
-                                    ]
-                                );
-                                if (typeof robj.ilsevent != 'undefined') {
-                                    throw(robj);
-                                }
-
-                                obj.render_pending_records(); // FIXME -- need a generic refresh for lists
-                                setTimeout(
-                                    function() {
-                                        JSAN.use('util.widgets'); 
-                                        util.widgets.dispatch('change_bucket',obj.controller.view.bucket_menulist);
-                                    }, 0
-                                );
                             } catch(E) {
                                 obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.record_buckets.merge_records.catch.std_unex_error'),E);
                             }
@@ -849,6 +801,25 @@
                             }
                         }
                     ],
+                    'cmd_marc_batch_edit' : [
+                        ['command'],
+                        function() {
+                            try {
+                                var bucket_id = obj.controller.view.bucket_menulist.value;
+                                if (!bucket_id) return;
+                                obj.list2.select_all();
+                                xulG.new_tab(
+                                    urls.MARC_BATCH_EDIT + '?containerid='+bucket_id+'&recordSource=b', 
+                                    {
+                                        'tab_name' : $('offlineStrings').getString('menu.cmd_marc_batch_edit.tab')
+                                    },
+                                    {}
+                                );
+                            } catch(E) {
+                                alert('Error in record_buckets.js, cmd_marc_batch_edit: ' + E);
+                            }
+                        }
+                    ],
                     'cmd_transfer_title_holds' : [
                         ['command'],
                         function() {

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets_overlay.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets_overlay.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/record_buckets_overlay.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -44,6 +44,10 @@
     <command id="cmd_add_sel_pending_to_record_bucket" />
 
     <command id="cmd_merge_records" disabled="true" />
+    <command id="cmd_marc_batch_edit"
+        label="&staff.cat.record_buckets_overlay.marc_batch_edit.label;" 
+        accesskey="&staff.cat.record_buckets_overlay.marc_batch_edit.accesskey;" 
+        disabled="true" />
     <command id="cmd_transfer_title_holds" 
         label="&staff.cat.record_buckets_overlay.transfer_title_holds.label;" 
         accesskey="&staff.cat.record_buckets_overlay.transfer_title_holds.accesskey;" 
@@ -216,6 +220,7 @@
         <button command="cmd_transfer_title_holds" />
         <button command="cmd_delete_records" label="&staff.cat.record_buckets_overlay.del_records.label;"/>
         <button command="cmd_merge_records" label="&staff.cat.record_buckets_overlay.merge_records.label;"/>
+        <button command="cmd_marc_batch_edit" />
         <button id="record_buckets_export_records" label="&staff.cat.record_buckets_overlay.export_records.label;" type="menu" allowevents="true" disabled="true">
             <menupopup id="record_buckets_export_record_types" allowevents="true">
                 <menuitem command="cmd_export_records_usmarc" label="&staff.cat.record_buckets_overlay.menuitem.export_usmarc.label;"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -580,6 +580,7 @@
             w.xulG = { 
                 'url' : 'about:blank',
                 'show_print_button' : 1,
+                'printer_context' : 'label',
                 'alternate_print' : 1,
                 'no_xulG' : 1,
                 'title' : $("catStrings").getString('staff.cat.spine_labels.preview.title'),
@@ -609,6 +610,7 @@
                     'url' : 'data:text/html;charset=utf-8,'+window.escape(html),
                     'html_source' : html,
                     'show_print_button' : 1,
+                    'printer_context' : 'label',
                     'no_xulG' : 1
                 }
             );

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/spine_labels.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -35,7 +35,7 @@
 
     <vbox id="spine_labels_main" flex="1" class="my_overflow">
         <hbox flex="1" class="my_overflow">
-        <vbox>
+        <vbox id="before_splitter" oils_persist="width">
             <hbox>
                 <button label="&staff.cat.spine_labels.re-generate.label;"
                     accesskey="&staff.cat.spine_labels.re-generate.accesskey;" oncommand="generate()"/>
@@ -166,8 +166,8 @@
             </rows></grid>
             <button label="&staff.cat.spine_labels.available_macros.label;" oncommand="show_macros()"/>
         </vbox>
-        <splitter><grippy/></splitter>
-        <vbox id="panel" flex="1" class="my_overflow"/>
+        <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter panel"><grippy/></splitter>
+        <vbox id="panel" flex="1" class="my_overflow" oils_persist="width"/>
         </hbox>
     </vbox>
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/util.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/util.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/util.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -260,7 +260,7 @@
     data.stash('cb_temp_copy_ids');
     win.open( 
         xulG.url_prefix(urls.XUL_COPY_BUCKETS_QUICK),
-        'sel_bucket_win' + win.window_name_increment(),
+        '_blank',
         'chrome,resizable,center'
     );
 }
@@ -269,7 +269,7 @@
     JSAN.use('util.window'); var win = new util.window();
     win.open(
         xulG.url_prefix(urls.XUL_RECORD_BUCKETS_QUICK),
-        'sel_bucket_win' + win.window_name_increment(),
+        '_blank',
         'chrome,resizable,modal,center',
         {
             record_ids: record_ids 
@@ -770,6 +770,7 @@
 
         if (r == 0) {
             var count = 0;
+            JSAN.use('cat.util');
             for (var i = 0; i < copies.length; i++) {
                 try {
                     var robj = network.simple_request('MARK_ITEM_MISSING_PIECES',[ses(),copies[i].id()]);
@@ -781,12 +782,7 @@
                                 print.simple( robj.payload.slip.template_output().data() );
                             }
                             // Item Note
-                            win.open(
-                                urls.XUL_COPY_NOTES,
-                                $("catStrings").getString("staff.cat.copy_editor.copy_notes"),
-                                'chrome,resizable,modal',
-                                { 'copy_id' : copies[i].id() }
-                            );
+                            cat.util.spawn_copy_editor( { 'copy_ids' : [ copies[i].id() ], 'edit' : 1 } );
                             // Patron Message
                             var my_xulG = win.open(
                                 urls.XUL_NEW_STANDING_PENALTY,

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_buckets.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_buckets.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_buckets.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="example_template_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,8 +18,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="cat_volume_copy_creator_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
-    width="800" height="580"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+    width="800" height="580" oils_persist="height width sizemode"
     title="&staff.cat.volume_copy_creator.title;"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -61,7 +61,19 @@
                             document.getElementById('sel_clip').setAttribute('disabled', sel.length < 1);
                             var list = util.functional.map_list(
                                 sel,
-                                function(o) { return o.getAttribute('retrieve_id'); }
+                                function(o) {
+                                    if ( $('jacket_image') ) {
+                                        // A side-effect in this map function, mu hahaha
+                                        if (o.getAttribute('isbn')) {
+                                            $('jacket_image').setAttribute('src',urls.ac_jacket_large+o.getAttribute('isbn'));
+                                            $('jacket_image').setAttribute('tooltiptext',urls.ac_jacket_large+o.getAttribute('isbn'));
+                                        } else {
+                                            $('jacket_image').setAttribute('src','');
+                                            $('jacket_image').setAttribute('tooltiptext','');
+                                        }
+                                    }
+                                    return o.getAttribute('retrieve_id');
+                                }
                             );
                             obj.error.sdump('D_TRACE','cat/z3950: selection list = ' + js2JSON(list) );
                             obj.controller.view.marc_import.disabled = false;
@@ -656,6 +668,7 @@
                                 }
                             }
                         );
+                        n.my_node.setAttribute('isbn', function(a){return a;}(obj.result_set[ obj.number_of_result_sets ].records[j].mvr).isbn());
                         if (!f) { n.my_node.parentNode.focus(); f = n; } 
                     }
                 } else {
@@ -705,119 +718,126 @@
 
     'spawn_marc_editor' : function(my_marcxml,biblio_source) {
         var obj = this;
-        xulG.new_tab(
-            xulG.url_prefix(urls.XUL_MARC_EDIT), 
-            { 'tab_name' : 'MARC Editor' }, 
-            { 
-                'record' : { 'marc' : my_marcxml, "rtype": "bre" },
-                'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
-                    try {
-                        JSAN.use('cat.util'); return cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
-                    } catch(E) {
-                        alert(E);
-                    }
-                },
-                'save' : {
-                    'label' : 'Import Record',
-                    'func' : function (new_marcxml) {
-                        try {
-                            var r = obj.network.simple_request('MARC_XML_RECORD_IMPORT', [ ses(), new_marcxml, biblio_source ]);
-                            if (typeof r.ilsevent != 'undefined') {
-                                switch(Number(r.ilsevent)) {
-                                    case 1704 /* TCN_EXISTS */ :
-                                        var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.same_tcn', [r.payload.tcn]);
-                                        var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.title');
-                                        var btn1 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn1_overlay');
-                                        var btn2 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.btn2_import', [r.payload.new_tcn]);
-                                        if (btn2) {
-                                            obj.data.init({'via':'stash'});
-                                            var robj = obj.network.simple_request(
-                                                'PERM_CHECK',[
-                                                    ses(),
-                                                    obj.data.list.au[0].id(),
-                                                    obj.data.list.au[0].ws_ou(),
-                                                    [ 'ALLOW_ALT_TCN' ]
-                                                ]
-                                            );
-                                            if (typeof robj.ilsevent != 'undefined') {
-                                                obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.permission_error'),E);
+
+        function save_marc (new_marcxml) {
+            try {
+                var r = obj.network.simple_request('MARC_XML_RECORD_IMPORT', [ ses(), new_marcxml, biblio_source ]);
+                if (typeof r.ilsevent != 'undefined') {
+                    switch(Number(r.ilsevent)) {
+                        case 1704 /* TCN_EXISTS */ :
+                            var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.same_tcn', [r.payload.tcn]);
+                            var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.title');
+                            var btn1 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn1_overlay');
+                            var btn2 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.btn2_import', [r.payload.new_tcn]);
+                            if (btn2) {
+                                obj.data.init({'via':'stash'});
+                                var robj = obj.network.simple_request(
+                                    'PERM_CHECK',[
+                                        ses(),
+                                        obj.data.list.au[0].id(),
+                                        obj.data.list.au[0].ws_ou(),
+                                        [ 'ALLOW_ALT_TCN' ]
+                                    ]
+                                );
+                                if (typeof robj.ilsevent != 'undefined') {
+                                    obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.permission_error'),E);
+                                }
+                                if (robj.length != 0) btn2 = null;
+                            }
+                            var btn3 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn3_cancel_import');
+                            var p = obj.error.yns_alert(msg,title,btn1,btn2,btn3,$("catStrings").getString('staff.cat.z3950.spawn_marc_editor.confirm_action'));
+                            obj.error.sdump('D_ERROR','option ' + p + 'chosen');
+                            switch(p) {
+                                case 0:
+                                    var r3 = obj.network.simple_request('MARC_XML_RECORD_UPDATE', [ ses(), r.payload.dup_record, new_marcxml, biblio_source ]);
+                                    if (typeof r3.ilsevent != 'undefined') {
+                                        throw(r3);
+                                    } else {
+                                        alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_overlay'));
+                                        return {
+                                            'id' : r3.id(),
+                                            'on_complete' : function() {
+                                                try {
+                                                    obj.replace_tab_with_opac(r3.id());
+                                                } catch(E) {
+                                                    alert(E);
+                                                }
                                             }
-                                            if (robj.length != 0) btn2 = null;
-                                        }
-                                        var btn3 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn3_cancel_import');
-                                        var p = obj.error.yns_alert(msg,title,btn1,btn2,btn3,$("catStrings").getString('staff.cat.z3950.spawn_marc_editor.confirm_action'));
-                                        obj.error.sdump('D_ERROR','option ' + p + 'chosen');
-                                        switch(p) {
-                                            case 0:
-                                                var r3 = obj.network.simple_request('MARC_XML_RECORD_UPDATE', [ ses(), r.payload.dup_record, new_marcxml, biblio_source ]);
-                                                if (typeof r3.ilsevent != 'undefined') {
-                                                    throw(r3);
-                                                } else {
-                                                    alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_overlay'));
-                                                    return {
-                                                        'id' : r3.id(),
-                                                        'on_complete' : function() {
-                                                            try {
-                                                                obj.replace_tab_with_opac(r3.id());
-                                                            } catch(E) {
-                                                                alert(E);
-                                                            }
-                                                        }
-                                                    };
+                                        };
+                                    }
+                                break;
+                                case 1:
+                                    var r2 = obj.network.request(
+                                        api.MARC_XML_RECORD_IMPORT.app,
+                                        api.MARC_XML_RECORD_IMPORT.method + '.override',
+                                        [ ses(), new_marcxml, biblio_source ]
+                                    );
+                                    if (typeof r2.ilsevent != 'undefined') {
+                                        throw(r2);
+                                    } else {
+                                        alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import_with_new_tcn'));
+                                        return {
+                                            'id' : r2.id(),
+                                            'on_complete' : function() {
+                                                try {
+                                                    obj.replace_tab_with_opac(r2.id());
+                                                } catch(E) {
+                                                    alert(E);
                                                 }
-                                            break;
-                                            case 1:
-                                                var r2 = obj.network.request(
-                                                    api.MARC_XML_RECORD_IMPORT.app,
-                                                    api.MARC_XML_RECORD_IMPORT.method + '.override',
-                                                    [ ses(), new_marcxml, biblio_source ]
-                                                );
-                                                if (typeof r2.ilsevent != 'undefined') {
-                                                    throw(r2);
-                                                } else {
-                                                    alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import_with_new_tcn'));
-                                                    return {
-                                                        'id' : r2.id(),
-                                                        'on_complete' : function() {
-                                                            try {
-                                                                obj.replace_tab_with_opac(r2.id());
-                                                            } catch(E) {
-                                                                alert(E);
-                                                            }
-                                                        }
-                                                    };
-                                                }
-                                            break;
-                                            case 2:
-                                            default:
-                                                alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_cancelled'));
-                                            break;
-                                        }
-                                    break;
-                                    default:
-                                        throw(r);
-                                    break;
-                                }
-                            } else {
-                                alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import'));
-                                return {
-                                    'id' : r.id(),
-                                    'on_complete' : function() {
-                                        try {
-                                            obj.replace_tab_with_opac(r.id());
-                                        } catch(E) {
-                                            alert(E);
-                                        }
+                                            }
+                                        };
                                     }
-                                };
+                                break;
+                                case 2:
+                                default:
+                                    alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_cancelled'));
+                                break;
                             }
+                        break;
+                        default:
+                            throw(r);
+                        break;
+                    }
+                } else {
+                    alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import'));
+                    return {
+                        'id' : r.id(),
+                        'on_complete' : function() {
+                            try {
+                                obj.replace_tab_with_opac(r.id());
+                            } catch(E) {
+                                alert(E);
+                            }
+                        }
+                    };
+                }
+            } catch(E) {
+                obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_error'),E);
+            }
+        };
+
+        if ( $('marc_editor').checked ) {
+            xulG.new_tab(
+                xulG.url_prefix(urls.XUL_MARC_EDIT), 
+                { 'tab_name' : 'MARC Editor' }, 
+                { 
+                    'record' : { 'marc' : my_marcxml, "rtype": "bre" },
+                    'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
+                        try {
+                            JSAN.use('cat.util'); return cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
                         } catch(E) {
-                            obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_error'),E);
+                            alert(E);
                         }
+                    },
+                    'save' : {
+                        'label' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.save_button_label'),
+                        'func' : save_marc
                     }
-                }
-            } 
-        );
+                } 
+            );
+        } else {
+            save_marc(my_marcxml);
+        }
     },
 
     'confirm_overlay' : function(record_ids) {
@@ -865,100 +885,119 @@
             return;
         }
 
-        xulG.new_tab(
-            xulG.url_prefix(urls.XUL_MARC_EDIT), 
-            { 'tab_name' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.tab_name') },
-            { 
-                'record' : { 'marc' : my_marcxml },
-                'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
+        function overlay_marc (new_marcxml) {
+            try {
+                if (! obj.confirm_overlay( [ obj.data.marked_record ] ) ) { return; }
+                var r = obj.network.simple_request('MARC_XML_RECORD_REPLACE', [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]);
+                if (typeof r.ilsevent != 'undefined') {
+                    switch(Number(r.ilsevent)) {
+                        case 1704 /* TCN_EXISTS */ :
+                            var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.same_tcn', [r.payload.tcn]);
+                            var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.import_collision');
+                            var btn1 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn1_overlay', [r.payload.new_tcn]);
+                            if (btn1) {
+                                var robj = obj.network.simple_request(
+                                    'PERM_CHECK',[
+                                        ses(),
+                                        obj.data.list.au[0].id(),
+                                        obj.data.list.au[0].ws_ou(),
+                                        [ 'ALLOW_ALT_TCN' ]
+                                    ]
+                                );
+                                if (typeof robj.ilsevent != 'undefined') {
+                                    obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.permission_error'),E);
+                                }
+                                if (robj.length != 0) btn1 = null;
+                            }
+                            var btn2 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn2_cancel');
+                            var p = obj.error.yns_alert(msg,title,btn1,btn2,null, $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.confirm_action'));
+                            obj.error.sdump('D_ERROR','option ' + p + 'chosen');
+                            switch(p) {
+                                case 0:
+                                    var r2 = obj.network.request(
+                                        api.MARC_XML_RECORD_REPLACE.app,
+                                        api.MARC_XML_RECORD_REPLACE.method + '.override',
+                                        [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]
+                                    );
+                                    if (typeof r2.ilsevent != 'undefined') {
+                                        throw(r2);
+                                    } else {
+                                        alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.successful_overlay_with_new_TCN'));
+                                        return {
+                                            'id' : r2.id(),
+                                            'on_complete' : function() {
+                                                try {
+                                                    obj.replace_tab_with_opac(r2.id());
+                                                } catch(E) {
+                                                    alert(E);
+                                                }
+                                            }
+                                        };
+                                    }
+                                break;
+                                case 1:
+                                default:
+                                    alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.cancelled_overlay'));
+                                break;
+                            }
+                        break;
+                        default:
+                            throw(r);
+                        break;
+                    }
+                } else {
+                    alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.success_overlay'));
                     try {
-                        JSAN.use('cat.util'); cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
+                        obj.data.marked_record_mvr = null;
+                        obj.data.marked_record = null;
+                        obj.data.stash('marked_record');
+                        obj.data.stash('marked_record_mvr');
+                        obj.controller.view.marc_import_overlay.disabled = true;
+                        if ($("overlay_tcn_indicator")) {
+                            $("overlay_tcn_indicator").setAttribute('value',$("catStrings").getString('staff.cat.z3950.marked_record_for_overlay_indicator.no_record.label'));
+                        }
+                        xulG.set_statusbar(1, $("catStrings").getString('staff.cat.z3950.marked_record_for_overlay_indicator.no_record.label') );
                     } catch(E) {
-                        alert(E);
+                        dump('Error in z3950.js, post-overlay: ' + E + '\n');
                     }
-                },
-                'save' : {
-                    'label' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_record_label'),
-                    'func' : function (new_marcxml) {
+                    return {
+                        'id' : r.id(),
+                        'on_complete' : function() {
+                            try {
+                                obj.replace_tab_with_opac(r.id());
+                            } catch(E) {
+                                alert(E);
+                            }
+                        }
+                    };
+                }
+            } catch(E) {
+                obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_error'),E);
+            }
+        }
+
+        if ( $('marc_editor').checked ) {
+            xulG.new_tab(
+                xulG.url_prefix(urls.XUL_MARC_EDIT), 
+                { 'tab_name' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.tab_name') },
+                { 
+                    'record' : { 'marc' : my_marcxml },
+                    'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
                         try {
-                            if (! obj.confirm_overlay( [ obj.data.marked_record ] ) ) { return; }
-                            var r = obj.network.simple_request('MARC_XML_RECORD_REPLACE', [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]);
-                            if (typeof r.ilsevent != 'undefined') {
-                                switch(Number(r.ilsevent)) {
-                                    case 1704 /* TCN_EXISTS */ :
-                                        var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.same_tcn', [r.payload.tcn]);
-                                        var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.import_collision');
-                                        var btn1 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn1_overlay', [r.payload.new_tcn]);
-                                        if (btn1) {
-                                            var robj = obj.network.simple_request(
-                                                'PERM_CHECK',[
-                                                    ses(),
-                                                    obj.data.list.au[0].id(),
-                                                    obj.data.list.au[0].ws_ou(),
-                                                    [ 'ALLOW_ALT_TCN' ]
-                                                ]
-                                            );
-                                            if (typeof robj.ilsevent != 'undefined') {
-                                                obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.permission_error'),E);
-                                            }
-                                            if (robj.length != 0) btn1 = null;
-                                        }
-                                        var btn2 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn2_cancel');
-                                        var p = obj.error.yns_alert(msg,title,btn1,btn2,null, $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.confirm_action'));
-                                        obj.error.sdump('D_ERROR','option ' + p + 'chosen');
-                                        switch(p) {
-                                            case 0:
-                                                var r2 = obj.network.request(
-                                                    api.MARC_XML_RECORD_REPLACE.app,
-                                                    api.MARC_XML_RECORD_REPLACE.method + '.override',
-                                                    [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]
-                                                );
-                                                if (typeof r2.ilsevent != 'undefined') {
-                                                    throw(r2);
-                                                } else {
-                                                    alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.successful_overlay_with_new_TCN'));
-                                                    return {
-                                                        'id' : r2.id(),
-                                                        'on_complete' : function() {
-                                                            try {
-                                                                obj.replace_tab_with_opac(r2.id());
-                                                            } catch(E) {
-                                                                alert(E);
-                                                            }
-                                                        }
-                                                    };
-                                                }
-                                            break;
-                                            case 1:
-                                            default:
-                                                alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.cancelled_overlay'));
-                                            break;
-                                        }
-                                    break;
-                                    default:
-                                        throw(r);
-                                    break;
-                                }
-                            } else {
-                                alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.success_overlay'));
-                                return {
-                                    'id' : r.id(),
-                                    'on_complete' : function() {
-                                        try {
-                                            obj.replace_tab_with_opac(r.id());
-                                        } catch(E) {
-                                            alert(E);
-                                        }
-                                    }
-                                };
-                            }
+                            JSAN.use('cat.util'); cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
                         } catch(E) {
-                            obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_error'),E);
+                            alert(E);
                         }
+                    },
+                    'save' : {
+                        'label' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_record_label'),
+                        'func' : overlay_marc
                     }
-                }
-            } 
-        );
+                } 
+            );
+        } else {
+            overlay_marc(my_marcxml);
+        }
     },
 
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/cat/z3950.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -131,7 +131,7 @@
                         <button id="search" label="&staff.cat.z3950.search.label;" accesskey="&staff.cat.z3950.search.accesskey;" disabled="true"/>
                     </hbox>
                 </groupbox>
-                <splitter id="x_splitter" collapse="after" oils_persist="state hidden"><grippy id="splitter_grippy1"/></splitter>
+                <splitter id="x_splitter" collapse="after" oils_persist="state hidden" oils_persist_peers="x_splitter1 x_splitter2"><grippy id="splitter_grippy1"/></splitter>
                 <groupbox id="x_splitter2" oils_persist="width" flex="1">
                     <caption label="&staff.cat.z3950.service_credentials.label;"/>
                     <grid flex="1">
@@ -159,7 +159,7 @@
                     </hbox>
                 </groupbox>
             </hbox>
-            <splitter id="z_splitter" collapse="before" oils_persist="state hidden"><grippy id="splitter_grippy2"/></splitter>
+            <splitter id="z_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="top_pane z_splitter2"><grippy id="splitter_grippy2"/></splitter>
             <groupbox id="z_splitter2" oils_persist="height" flex="1">
                 <caption label="&staff.cat.z3950.results_caption.label;"/>
                 <hbox>
@@ -183,14 +183,19 @@
                     </button>
                     <spacer flex="1"/>
                     <button id="marc_view_btn" command="marc_view" disabled="true"/>
+                    <checkbox id="marc_editor" label="&staff.cat.z3950.marc_editor.label;" accesskey="&staff.cat.z3950.marc_editor.accesskey;" oils_persist="checked" checked="true"/> 
                     <button id="marc_import_overlay" label="&staff.cat.z3950.marc_import_overlay.label;" accesskey="&staff.cat.z3950.marc_import_overlay.accesskey;" disabled="true"/>
                     <button id="marc_import" label="&staff.cat.z3950.result_message.marc_import.label;" accesskey="&staff.cat.z3950.result_message.marc_import.accesskey;" disabled="true"/>
                     <button id="toggle_form_btn" command="toggle_form"/>
                 </hbox>
-                <deck id="deck" flex="1">
-                    <tree id="results" flex="1" enableColumnDrag="true" seltype="single"/>
-                    <iframe id="marc_frame" src="/xul/server/cat/marc_view.html" flex="1"/>
-                </deck>
+                <hbox flex="1">
+                    <image id="jacket_image" oils_persist="width"/>
+                    <splitter id="jacket_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="jacket_image deck"><grippy id="jacket_splitter_grippy" /></splitter>
+                    <deck id="deck" flex="1" oils_persist="width">
+                        <tree id="results" flex="1" enableColumnDrag="true" seltype="single"/>
+                        <iframe id="marc_frame" src="/xul/server/cat/marc_view.html" flex="1"/>
+                    </deck>
+                </hbox>
             </groupbox>
     </groupbox>
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -1,6 +1,8 @@
 var error; 
 var network;
 var data;
+var transit_list;
+var hold_list;
 
 function my_init() {
     try {
@@ -39,6 +41,16 @@
             );
         }
 
+        JSAN.use('circ.util'); 
+        JSAN.use('util.list'); 
+
+        var columns = circ.util.transit_columns({});
+        transit_list = new util.list('transit');
+        transit_list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
+
+        hold_list = new util.list('hold');
+        hold_list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
+
         // timeout so xulG gets a chance to get pushed in
         setTimeout(
             function () { xulG.from_item_details_new = false; load_item(); },
@@ -169,7 +181,11 @@
             set("copy_circ_lib" , typeof details.copy.circ_lib() == 'object' ? details.copy.circ_lib().shortname() : data.hash.aou[ details.copy.circ_lib() ].shortname()); 
             set_tooltip("copy_circ_lib" , typeof details.copy.circ_lib() == 'object' ? details.copy.circ_lib().name() : data.hash.aou[ details.copy.circ_lib() ].name()); 
             var cm = details.copy.circ_modifier();
-            set("circ_modifier", document.getElementById('commonStrings').getFormattedString('staff.circ_modifier.display',[cm,data.hash.ccm[cm].name(),data.hash.ccm[cm].description()])); 
+            if (typeof data.hash.ccm[cm] != 'undefined') {
+                set("circ_modifier", document.getElementById('commonStrings').getFormattedString('staff.circ_modifier.display',[cm,data.hash.ccm[cm].name(),data.hash.ccm[cm].description()])); 
+            } else {
+                set("circ_modifier","");
+            }
             set("circulate", get_localized_bool( details.copy.circulate() )); 
             set("copy_number", details.copy.copy_number()); 
             set("copy_create_date", util.date.formatted_date( details.copy.create_date(), '%{localized}' )); 
@@ -285,11 +301,9 @@
         set("hold_transit_copy", '');
 
         if (details.transit) {
-            JSAN.use('circ.util'); var columns = circ.util.transit_columns({});
 
-            JSAN.use('util.list'); var list = new util.list('transit');
-            list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
-            list.append( { 'row' : { 'my' : { 'atc' : details.transit, } } });
+            transit_list.clear();
+            transit_list.append( { 'row' : { 'my' : { 'atc' : details.transit, } } });
 
             var transit_copy_status = typeof details.transit.copy_status() == 'object' ? details.transit.copy_status() : data.hash.ccs[ details.transit.copy_status() ];
                 set("transit_copy_status", transit_copy_status.name() );
@@ -589,9 +603,8 @@
                 } 
             );
 
-            JSAN.use('util.list'); var list = new util.list('hold');
-            list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
-            list.append( { 'row' : { 'my' : { 'ahr' : better_fleshed_hold_blob.hold, 'acp' : details.copy, 'status' : status_robj, } } });
+            hold_list.clear();
+            hold_list.append( { 'row' : { 'my' : { 'ahr' : better_fleshed_hold_blob.hold, 'acp' : details.copy, 'status' : status_robj, } } });
 
             JSAN.use('patron.util'); 
             var au_obj = patron.util.retrieve_fleshed_au_via_id( ses(), details.hold.usr() );

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,7 +19,7 @@
 <?xul-overlay href="/xul/server/cat/bib_brief_overlay.xul"?>
 
 <window id="alt_copy_summary_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
@@ -282,14 +282,14 @@
                     </grid>
                 </tabpanel>
                 <tabpanel orient="vertical"><!-- Hold/Transit -->
-                    <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;">
+                    <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;" oils_persist="height">
                         <caption id="hold_caption" label="&staff.circ.copy_details.hold_caption;"/>
                         <label id="hold_patron_name" class="patronNameLarge"/>
                         <tree id="hold" flex="1" enableColumnDrag="true"/>
                         <spacer FIXME="label and tree get swapped without this"/>
                     </groupbox>
-                    <splitter><grippy/></splitter>
-                    <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;">
+                    <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="holds transits"><grippy/></splitter>
+                    <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;" oils_persist="height">
                         <caption id="transit_caption" label="&staff.circ.copy_details.transit_caption;"/>
                         <tree id="transit" flex="1" enableColumnDrag="true"/>
                     </groupbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_brief.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_brief.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_brief.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,7 +19,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="circ_circ_brief_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns:html="http://www.w3.org/1999/xhtml"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_summary.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_summary.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/circ_summary.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,8 +19,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="circ_circ_brief_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
-    width="750" height="550"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+    width="750" height="550" oils_persist="height width sizemode"
     xmlns:html="http://www.w3.org/1999/xhtml"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
@@ -137,11 +137,11 @@
     </script>
 
     <vbox flex="1" class="my_overflow">
-        <vbox id="top_vbox" flex="1" class="my_overflow"/>
-        <splitter><grippy/></splitter>
-        <vbox id="mid_vbox" flex="1" class="my_overflow"/>
-        <splitter><grippy/></splitter>
-        <groupbox flex="1" id="circs" class="my_overflow">
+        <vbox id="top_vbox" flex="1" class="my_overflow" oils_persist="height"/>
+        <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="top_vbox mid_vbox"><grippy/></splitter>
+        <vbox id="mid_vbox" flex="1" class="my_overflow" oils_persist="height"/>
+        <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="mid_vbox circs"><grippy/></splitter>
+        <groupbox flex="1" id="circs" class="my_overflow" oils_persist="height">
             <caption label="&staff.circ.circ_summary.caption;"/>
         </groupbox>
         <hbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/copy_details.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/copy_details.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/copy_details.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -197,23 +197,23 @@
     <messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties" />
 
     <vbox flex="1" style="overflow: auto;">
-        <vbox id="top_box" flex="1" style="border: none; overflow: none; min-height: 80;"/>
-        <splitter><grippy/></splitter>
-        <vbox id="item_summary_box" flex="1" style="border: none; overflow: none; min-height: 80;"/>
-        <splitter><grippy/></splitter>
-        <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;">
+        <vbox id="top_box" flex="1" style="border: none; overflow: none; min-height: 80;" oils_persist="height"/>
+        <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="top_box item_summary_box"><grippy/></splitter>
+        <vbox id="item_summary_box" flex="1" style="border: none; overflow: none; min-height: 80;" oils_persist="height"/>
+        <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="item_summary_box holds"><grippy/></splitter>
+        <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;" oils_persist="height">
             <caption id="hold_caption" label="&staff.circ.copy_details.hold_caption;"/>
             <label id="patron_name" class="patronNameLarge"/>
             <tree id="hold" flex="1" enableColumnDrag="true"/>
             <spacer FIXME="label and tree get swapped without this"/>
         </groupbox>
-        <splitter><grippy/></splitter>
-        <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;">
+        <splitter id="splitter3" oils_persist="state hidden" oils_persist_peers="holds transits"><grippy/></splitter>
+        <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;" oils_persist="height">
             <caption id="transit_caption" label="&staff.circ.copy_details.transit_caption;"/>
             <tree id="transit" flex="1" enableColumnDrag="true"/>
         </groupbox>
-        <splitter><grippy/></splitter>
-        <groupbox flex="1" id="circs" style="overflow: none; min-height: 80;">
+        <splitter id="splitter4" oils_persist="state hidden" oils_persist_peers="transits circs"><grippy/></splitter>
+        <groupbox flex="1" id="circs" style="overflow: none; min-height: 80;" oils_persist="height">
             <caption id="circ_caption" label="&staff.circ.copy_details.circ_caption;" style="font-weight: bold"/>
             <vbox id="circ_box" flex="1" style="min-height: 80"/>
         </groupbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/missing_pieces.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/missing_pieces.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/missing_pieces.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="main_test_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/pre_cat_fields.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/pre_cat_fields.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/pre_cat_fields.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -21,8 +21,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="pre_cat_fields" title="&staff.circ.pre_cat.window.title;"
-    orient="vertical" style="overflow: auto"
-    onload="try{my_init();font_helper(); }catch(E){alert(E);}"
+    orient="vertical" style="overflow: auto" oils_persist="height width sizemode"
+    onload="try{ my_init(); font_helper(); persist_helper(); }catch(E){alert(E);}"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/print_list_template_editor.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/print_list_template_editor.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/print_list_template_editor.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="print_list_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/circ/util.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/circ/util.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/circ/util.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -2089,6 +2089,15 @@
             'editable' : false, 'render' : function(my) { return my.patron_family_name ? my.patron_family_name : ""; }
         },
         {
+            "persist": "hidden width ordinal",
+            "id": "patron_alias",
+            'label' : document.getElementById('circStrings').getString('staff.circ.utils.patron_alias'),
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : true,
+            'editable' : false, 'render' : function(my) { return my.patron_alias ? my.patron_alias : ""; }
+        },
+        {
             'persist' : 'hidden width ordinal',
             'id' : 'patron_first_given_name',
             'label' : document.getElementById('circStrings').getString('staff.circ.utils.patron_first_given_name'),
@@ -2409,6 +2418,8 @@
             'route_to' : '',
             'route_to_msg' : '',
             'route_to_org_fullname' : '',
+            'destination_shelf' : '',
+            'destination_shelf_msg' : '',
             'courier_code' : '',
             'street1' : '',
             'street2' : '',
@@ -2506,16 +2517,24 @@
                             if (behind_the_desk_support) {
                                var usr_settings = network.simple_request('FM_AUS_RETRIEVE',[ses(),check.payload.hold.usr()]); 
                                 if (typeof usr_settings['circ.holds_behind_desk'] != 'undefined') {
-                                    print_data.prefer_behind_holds_desk = true;
-                                    check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.private_hold_shelf');
-                                    print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
-                                    print_data.route_to = check.route_to;
+                                    if (usr_settings['circ.holds_behind_desk']) {
+                                        print_data.prefer_behind_holds_desk = true;
+                                        check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.private_hold_shelf');
+                                        print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
+                                        print_data.route_to = check.route_to;
+                                    } else {
+                                        check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
+                                        print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
+                                        print_data.route_to = check.route_to;
+                                    }
                                 } else {
                                     check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
                                     print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
                                     print_data.route_to = check.route_to;
                                 }
                             }
+                            print_data.destination_shelf_msg = print_data.route_to_msg;
+                            print_data.destination_shelf = print_data.route_to;
                             msg += print_data.route_to_msg;
                             msg += '\n';
                         }
@@ -2891,6 +2910,26 @@
                 JSAN.use('patron.util');
                 var au_obj = patron.util.retrieve_fleshed_au_via_id( session, check.payload.hold.usr() );
                 print_data.user = au_obj;
+                print_data.user_stat_cat_entries = [];
+                var entries = au_obj.stat_cat_entries();
+                for (var i = 0; i < entries.length; i++) {
+                    var stat_cat = data.hash.my_actsc[ entries[i].stat_cat() ];
+                    if (!stat_cat) {
+                        stat_cat = data.lookup('actsc', entries[i].stat_cat());
+                    }
+                    print_data.user_stat_cat_entries.push( { 
+                        'id' : entries[i].id(),
+                        'stat_cat' : {
+                            'id' : stat_cat.id(),
+                            'name' : stat_cat.name(),
+                            'opac_visible' : stat_cat.opac_visible(),
+                            'owner' : stat_cat.owner(),
+                            'usr_summary' : stat_cat.usr_summary()
+                        },
+                        'stat_cat_entry' : entries[i].stat_cat_entry(),
+                        'target_usr' : entries[i].target_usr() 
+                    } );
+                }
                 msg += '\n';
                 if (au_obj.alias()) {
                     print_data.hold_for_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.hold.patron_alias',  [au_obj.alias()]);
@@ -2950,6 +2989,29 @@
                 print_data.request_date_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.hold.request_date', [print_data.request_date]);
                 msg += print_data.request_date_msg;
                 msg += '\n';
+                var destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.hold_shelf');
+                print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+                print_data.destination_shelf = destination_shelf;
+                var behind_the_desk_support = String( data.hash.aous['circ.holds.behind_desk_pickup_supported'] ) == 'true';
+                if (behind_the_desk_support) {
+                   var usr_settings = network.simple_request('FM_AUS_RETRIEVE',[ses(),check.payload.hold.usr()]); 
+                    if (typeof usr_settings['circ.holds_behind_desk'] != 'undefined') {
+                        if (usr_settings['circ.holds_behind_desk']) {
+                            print_data.prefer_behind_holds_desk = true;
+                            destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.private_hold_shelf');
+                            print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+                            print_data.destination_shelf = destination_shelf;
+                        } else {
+                            destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
+                            print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+                            print_data.destination_shelf = destination_shelf;
+                        }
+                    } else {
+                        destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
+                        print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+                        print_data.destination_shelf = destination_shelf;
+                    }
+                }
             }
             var rv = 0;
             var suppress_popups = data.hash.aous['ui.circ.suppress_checkin_popups'];

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties	2010-10-18 14:05:06 UTC (rev 18372)
@@ -278,16 +278,7 @@
 staff.cat.record_buckets.new_bucket.bucket_prompt_title=Bucket Creation
 staff.cat.record_buckets.new_bucket.same_name_alert=You already have a bucket with that name.
 staff.cat.record_buckets.new_bucket.bucket_created=Bucket %1$s created.
-staff.cat.record_buckets.merge_records.merge_lead=Merge these records? (Select the "lead" record first)
-staff.cat.record_buckets.merge_records.button.label=Merge
-staff.cat.record_buckets.merge_records.cancel_button.label=Cancel
-staff.cat.record_buckets.merge_records.cancel_button.accesskey=C
-staff.cat.record_buckets.merge_records.lead_record_number=Lead Record? # %1$s
-staff.cat.record_buckets.merge_records.lead=Lead
 staff.cat.record_buckets.merge_records.fancy_prompt_title=Record Merging
-staff.cat.record_buckets.merge_records.fancy_prompt.alert=Merge Aborted
-staff.cat.record_buckets.merge_records.success=Records were successfully merged.
-staff.cat.record_buckets.merge_records.catch.std_unex_error=Records were not likely merged.
 staff.cat.record_buckets.delete_records.xml1=Delete these records?
 staff.cat.record_buckets.delete_records.button.label=Delete
 staff.cat.record_buckets.delete_records.cancel_button.label=Cancel
@@ -458,6 +449,7 @@
 staff.cat.z3950.handle_results.result_error=Error retrieving results.
 staff.cat.z3950.handle_results.search_result_error=Failure during search result handling.
 staff.cat.z3950.replace_tab_with_opac.tab_name=Retrieving title...
+staff.cat.z3950.spawn_marc_editor.save_button_label=Import Record
 staff.cat.z3950.spawn_marc_editor.same_tcn=A record with TCN %1$s already exists.\nFIXME: add record summary here
 staff.cat.z3950.spawn_marc_editor.title=Import Collision
 staff.cat.z3950.spawn_marc_editor.btn1_overlay=Overlay

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties	2010-10-18 14:05:06 UTC (rev 18372)
@@ -330,6 +330,7 @@
 staff.circ.utils.author.none=No Author?
 staff.circ.utils.notify_time=Last Notify Time
 staff.circ.utils.notify_count=Notices
+staff.circ.utils.patron_alias=Patron Alias
 staff.circ.utils.patron_family_name=Patron Last Name
 staff.circ.utils.patron_first_given_name=Patron First Name
 staff.circ.utils.checkin.override=Override Checkin Failure?

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/main/data.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/main/data.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/main/data.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -224,17 +224,13 @@
             }
 
             g.open_menu = function() {
-                try {
-                    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-                    var mframe = xulG.window.open(urls.XUL_MENU_FRAME
-                        + '?server='+window.escape(xulG.url),
-                        'main'+xulG.window.window_name_increment(),'chrome,resizable'
-                    );
-                    delete xulG['_sound']; // This came from util.error but I want menu.js to have its own
-                    mframe.xulG = xulG; // This is the xulG from main.js, with auth, url, and window
-                } catch(E) {
-                    alert(E);
-                }
+                delete xulG['_sound'];
+                netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+                    getService(Components.interfaces.nsIWindowMediator);
+                var eg_main = wm.getMostRecentWindow('eg_main');
+                eg_main.openTabs.unshift('init');
+                wm.getMostRecentWindow('eg_main').new_tabs(null);
             }
 
             g.data.init();

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/main/simple_auth.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/main/simple_auth.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/main/simple_auth.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -21,8 +21,8 @@
 <!-- OVERLAYS -->
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
-<window id="simple_auth_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="simple_auth_win" oils_persist="height width sizemode"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/main/verify_credentials.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/main/verify_credentials.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/main/verify_credentials.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -17,7 +17,7 @@
 <!-- OVERLAYS -->
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
-<window id="verify_win" onload="try { verify_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="verify_win" onload="try { verify_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/barcode_entry.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/barcode_entry.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/barcode_entry.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_barcode_entry_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill2.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill2.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill2.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -58,7 +58,7 @@
     <vbox flex="1" class="my_overflow">
         <groupbox orient="vertical" flex="1">
             <caption id="caption" label="&staff.patron.bill_interface.caption.label;"/>
-            <hbox>
+            <hbox id="before_splitter1" oils_persist="height">
                 <grid flex="1">
                     <columns flex="1">
                         <column/>
@@ -93,13 +93,13 @@
                                 <label value="&staff.patron.bills_overlay.payment_type.value;" class="emphasis1" accesskey="&staff.patron.bills_overlay.payment_type.accesskey;" control="payment_type"/>
                                 <menulist id="payment_type">
                                     <menupopup id="payment_type_menupopup">
-                                        <menuitem id="payment_type_menuitem1" label="&staff.patron.bills_overlay.cash.label;" value="cash_payment"/>
-                                        <menuitem id="payment_type_menuitem2" label="&staff.patron.bills_overlay.check.label;" value="check_payment"/>
-                                        <menuitem id="payment_type_menuitem3" label="&staff.patron.bills_overlay.credit_card.label;" value="credit_card_payment"/>
+                                        <menuitem id="payment_type_menuitem1" class="hide_patron_cash" label="&staff.patron.bills_overlay.cash.label;" value="cash_payment"/>
+                                        <menuitem id="payment_type_menuitem2" class="hide_patron_check" label="&staff.patron.bills_overlay.check.label;" value="check_payment"/>
+                                        <menuitem id="payment_type_menuitem3" class="hide_patron_credit_card" label="&staff.patron.bills_overlay.credit_card.label;" value="credit_card_payment"/>
                                         <menuitem id="payment_type_menuitem4" class="hide_patron_credit" label="&staff.patron.bills_overlay.patron_credit.label;" value="credit_payment" />
-                                        <menuitem id="payment_type_menuitem5" label="&staff.patron.bills_overlay.word.label;" value="work_payment"/>
-                                        <menuitem id="payment_type_menuitem6" label="&staff.patron.bills_overlay.forgive.label;" value="forgive_payment"/>
-                                        <menuitem id="payment_type_menuitem7" label="&staff.patron.bills_overlay.goods.label;" value="goods_payment"/>
+                                        <menuitem id="payment_type_menuitem5" class="hide_patron_work" label="&staff.patron.bills_overlay.word.label;" value="work_payment"/>
+                                        <menuitem id="payment_type_menuitem6" class="hide_patron_forgive" label="&staff.patron.bills_overlay.forgive.label;" value="forgive_payment"/>
+                                        <menuitem id="payment_type_menuitem7" class="hide_patron_goods" label="&staff.patron.bills_overlay.goods.label;" value="goods_payment"/>
                                     </menupopup>
                                 </menulist>
                             </row>
@@ -116,8 +116,8 @@
                     </hbox>
                 </groupbox>
             </hbox>
-            <splitter />
-            <vbox flex="1">
+            <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1" />
+            <vbox flex="1" id="after_splitter1" oils_persist="height">
                 <hbox>
                     <button id="bill_patron_btn" label="&staff.patron.bills_overlay.bill_patron.label;" accesskey="&staff.patron.bills_overlay.bill_patron.accesskey;" />
                     <button id="bill_history_btn" label="&staff.patron.bills_overlay.history.label;" accesskey="&staff.patron.bills_overlay.history.accesskey;" />
@@ -162,8 +162,8 @@
                     </button>
                 </hbox>
             </vbox>
-            <splitter />
-            <hbox>
+            <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_splitter1 after_splitter2" />
+            <hbox id="after_splitter2" oils_persist="height">
                 <vbox>
                     <hbox>
                         <label value='&staff.patron.bill_interface.voided_this_session.label;' class="emphasis1"/><label id="currently_voided" value="0.00"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_cc_info.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_cc_info.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_cc_info.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,8 +19,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_bill" title="&staff.patron.bill_cc_info.title;"
-    orient="vertical" style="overflow: auto"
-    onload="try{info_init(); font_helper();refresh_fields();}catch(E){alert(E);}"
+    orient="vertical" style="overflow: auto" oils_persist="height width sizemode"
+    onload="try{info_init(); font_helper(); refresh_fields(); persist_helper(); }catch(E){alert(E);}"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_check_info.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_check_info.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_check_info.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,8 +19,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_bill" title="&staff.patron.bill_check_info.title;"
-    orient="vertical" style="overflow: auto"
-    onload="try{info_init(); font_helper();}catch(E){alert(E);}"
+    orient="vertical" style="overflow: auto" oils_persist="height width sizemode"
+    onload="try{info_init(); font_helper(); persist_helper(); }catch(E){alert(E);}"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_details.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_details.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_details.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -20,8 +20,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 <?xul-overlay href="/xul/server/patron/bill_summary_overlay.xul"?>
 
-<window id="bill_details_win" width="700" height="550"
-    onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="bill_details_win" width="700" height="550" oils_persist="width height sizemode"
+    onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
@@ -37,15 +37,15 @@
     <vbox flex="1" class="my_overflow">
         <label id="patron_name" class="patronNameLarge"/>
 
-        <groupbox orient="vertical" flex="1" id="summary" />
+        <groupbox orient="vertical" flex="1" id="summary" oils_persist="height"/>
 
-        <splitter><grippy/></splitter>
+        <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="summary copy_summary_vbox"><grippy/></splitter>
 
         <vbox id="copy_summary_vbox" flex="1" />
 
-        <splitter id="copy_summary_splitter"><grippy/></splitter>
+        <splitter id="copy_summary_splitter" oils_persist="state hidden" oils_persist_peers="copy_summary_vbox after_copy_summary_splitter"><grippy/></splitter>
 
-            <groupbox orient="vertical" flex="2">
+            <groupbox id="after_copy_summary_splitter" oils_persist="height" orient="vertical" flex="2">
                 <caption label="&staff.patron.bill_details.bills.label;" style="color: red"/>
                 <tree id="bill_tree" flex="1" enableColumnDrag="true"/>
                 <hbox>
@@ -56,9 +56,9 @@
                 </hbox>
             </groupbox>
 
-            <splitter><grippy/></splitter>
+            <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_copy_summary_splitter after_splitter2"><grippy/></splitter>
 
-            <groupbox orient="vertical" flex="2">
+            <groupbox orient="vertical" flex="2" id="after_splitter2" oils_persist="height">
                 <caption label="&staff.patron.bill_details.payments.label;" style="color: green"/>
                 <tree id="payment_tree" flex="1" enableColumnDrag="true"/>
                 <hbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_wizard.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_wizard.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/bill_wizard.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -20,8 +20,8 @@
 <?xul-overlay href="/xul/server/patron/bill_summary_overlay.xul"?>
 
 <window id="patron_bill" title="&staff.patron.bill_wizard.title;"
-    orient="vertical" style="overflow: auto"
-    onload="try { patron_bill_init(); font_helper(); } catch(E) { alert(E); }" width="700" height="550"
+    orient="vertical" style="overflow: auto" oils_persist="width height sizemode"
+    onload="try { patron_bill_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" width="700" height="550"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -35,7 +35,7 @@
         <hbox id="left_deck_vbox" flex="1" oils_persist="height"> 
             <deck id="patron_left_deck" oils_persist="height"/>
         </hbox>
-        <splitter id="deck_splitter" collapse="before" oils_persist="state hidden"><grippy id="splitter_grippy"/></splitter>
+        <splitter id="deck_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="left_deck_vbox right_deck_vbox"><grippy id="splitter_grippy"/></splitter>
         <hbox id="right_deck_vbox" flex="3" oils_persist="height">
             <deck id="patron_right_deck" oils_persist="height"/>
         </hbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_overlay.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_overlay.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/display_overlay.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -35,7 +35,7 @@
         <vbox id="left_deck_vbox" flex="1" oils_persist="width"> 
             <deck id="patron_left_deck" oils_persist="width"/>
         </vbox>
-        <splitter id="deck_splitter" collapse="before" oils_persist="state hidden"><grippy id="splitter_grippy"/></splitter>
+        <splitter id="deck_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="left_deck_vbox right_deck_vbox"><grippy id="splitter_grippy"/></splitter>
         <vbox id="right_deck_vbox" flex="3" oils_persist="width">
             <deck id="patron_right_deck" oils_persist="width"/>
         </vbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/edit_standing_penalty.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/edit_standing_penalty.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/edit_standing_penalty.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="edit_penalty_win" 
-    onload="try { edit_penalty_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { edit_penalty_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+    oils_persist="height width sizemode"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
     title="&staff.patron_display.edit_penalty_dialog.title;">
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_cancel.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_cancel.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_cancel.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="hold_cancel_win" 
-    onload="try { hold_cancel_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { hold_cancel_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
     title="&staff.hold_list.cancel_hold_dialog.title;">
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -21,6 +21,10 @@
 
         if (xulG.ahr_id) fetch_and_render_all();
 
+        if (xul_param('when_done')) {
+            xul_param('when_done')();
+        }
+
     } catch(E) {
         try { g.error.standard_unexpected_error_alert('/xul/server/patron/hold_notices.xul',E); } catch(E) { alert('FIXME: ' + js2JSON(E)); }
     }
@@ -121,33 +125,39 @@
             },
         }
     );
+    dump('hold details init_list done\n');
 }
 
 function a_list_of_one() {
-    g.list.clear();
-    g.list.append(
-        {
-            'row' : {
-                'my' : {
-                    'ahr' : g.ahr,
-                    'status' : g.blob.status,
-                    'acp' : g.blob.copy,
-                    'acn' : g.blob.volume,
-                    'mvr' : g.blob.mvr,
-                    'patron_family_name' : g.blob.patron_last,
-                    'patron_first_given_name' : g.blob.patron_first,
-                    'patron_barcode' : g.blob.patron_barcode,
-                    'total_holds' : g.blob.total_holds,
-                    'queue_position' : g.blob.queue_position,
-                    'potential_copies' : g.blob.potential_copies,
-                    'estimated_wait' : g.blob.estimated_wait,
-                    'ahrn_count' : g.blob.hold.notes().length,
-                    'blob' : g.blob
-                }
-            },
-            'no_auto_select' : true,
-        }
-    );
+    try {
+        g.list.clear();
+        g.list.append(
+            {
+                'row' : {
+                    'my' : {
+                        'ahr' : g.ahr,
+                        'status' : g.blob.status,
+                        'acp' : g.blob.copy,
+                        'acn' : g.blob.volume,
+                        'mvr' : g.blob.mvr,
+                        'patron_family_name' : g.blob.patron_last,
+                        'patron_first_given_name' : g.blob.patron_first,
+                        'patron_barcode' : g.blob.patron_barcode,
+                        'patron_alias' : g.blob.patron_alias,
+                        'total_holds' : g.blob.total_holds,
+                        'queue_position' : g.blob.queue_position,
+                        'potential_copies' : g.blob.potential_copies,
+                        'estimated_wait' : g.blob.estimated_wait,
+                        'ahrn_count' : g.blob.hold.notes().length,
+                        'blob' : g.blob
+                    }
+                },
+                'no_auto_select' : true,
+            }
+        );
+    } catch(E) {
+        alert('Error in hold_details.js, a_list_of_one(): ' + E);
+    }
 }
 
 function retrieve_notifications() {

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/hold_details.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -65,7 +65,7 @@
         <vbox id="bib_brief_box" flex="1" style="min-height: 10em;"/>
     </vbox>
 
-    <splitter><grippy/></splitter>
+    <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="v1 v2"><grippy/></splitter>
 
     <vbox id="v2" flex="1" oils_persist="height">
         <vbox flex="1">
@@ -74,9 +74,9 @@
         </vbox>
     </vbox>
 
-    <splitter><grippy/></splitter>
+    <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="v2 after_splitter2"><grippy/></splitter>
 
-    <tabbox flex="1">
+    <tabbox flex="1" id="after_splitter2" oils_persist="height">
         <tabs>
             <tab label="&staff.patron.holds.notes_tab.label;" accesskey="&staff.patron.holds.notes_tab.accesskey;" />
             <tab label="&staff.patron.holds.notices_tab.label;" accesskey="&staff.patron.holds.notices_tab.accesskey;" />

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -85,6 +85,7 @@
                                     row.my.patron_family_name = blob.patron_last;
                                     row.my.patron_first_given_name = blob.patron_first;
                                     row.my.patron_barcode = blob.patron_barcode;
+                                    row.my.patron_alias = blob.patron_alias;
                                     row.my.total_holds = blob.total_holds;
                                     row.my.queue_position = blob.queue_position;
                                     row.my.potential_copies = blob.potential_copies;
@@ -150,7 +151,10 @@
                         obj.controller.view.cmd_holds_edit_request_date.setAttribute('disabled','false');
                         obj.controller.view.cmd_holds_activate.setAttribute('disabled','false');
                         obj.controller.view.cmd_holds_suspend.setAttribute('disabled','false');
-                        obj.controller.view.cmd_alt_view.setAttribute('disabled','false');
+                        obj.controller.view.cmd_alt_view.setAttribute('rendering_rows','false');
+                        if (obj.controller.view.cmd_alt_view.getAttribute('ready')=='true') {
+                            obj.controller.view.cmd_alt_view.setAttribute('disabled','false');
+                        }
                         obj.controller.view.cmd_holds_retarget.setAttribute('disabled','false');
                         obj.controller.view.cmd_holds_cancel.setAttribute('disabled','false');
                         obj.controller.view.cmd_holds_uncancel.setAttribute('disabled','false');
@@ -173,6 +177,7 @@
                         obj.controller.view.cmd_holds_activate.setAttribute('disabled','true');
                         obj.controller.view.cmd_holds_suspend.setAttribute('disabled','true');
                         obj.controller.view.cmd_alt_view.setAttribute('disabled','true');
+                        obj.controller.view.cmd_alt_view.setAttribute('rendering_rows','true');
                         obj.controller.view.cmd_holds_retarget.setAttribute('disabled','true');
                         obj.controller.view.cmd_holds_cancel.setAttribute('disabled','true');
                         obj.controller.view.cmd_holds_uncancel.setAttribute('disabled','true');
@@ -323,6 +328,43 @@
                             }
                         }
                     ],
+                    'cmd_holds_print_alt' : [
+                        ['command'],
+                        function() {
+                            try {
+                                var content_params = {
+                                    "session": ses(),
+                                    "authtime": ses("authtime"),
+                                    "no_xulG": false,
+                                    "show_nav_buttons": true,
+                                    "show_print_button": false
+                                };
+                                ["url_prefix", "new_tab", "set_tab",
+                                    "close_tab", "new_patron_tab",
+                                    "set_patron_tab", "volume_item_creator",
+                                    "get_new_session",
+                                    "holdings_maintenance_tab", "set_tab_name",
+                                    "open_chrome_window", "url_prefix",
+                                    "network_meter", "page_meter",
+                                    "set_statusbar", "set_help_context"
+                                ].forEach(function(k) {
+                                    content_params[k] = xulG[k];
+                                });
+
+                                var loc = urls.XUL_BROWSER + "?url=" + window.escape(
+                                    xulG.url_prefix("/opac/extras/circ/alt_holds_print.html").replace("http:","https:")
+                                );
+                                xulG.new_tab(
+                                    loc, {
+                                        "tab_name": "Printable Pull List", /* XXX i18n */
+                                        "browser": false
+                                    }, content_params
+                                );
+                            } catch (E) {
+                                g.error.sdump("D_ERROR", E);
+                            }
+                        }
+                    ],
                     'cmd_holds_print' : [
                         ['command'],
                         function() {
@@ -1293,6 +1335,7 @@
         var x_clear_shelf_widgets = document.getElementById('clear_shelf_widgets');
         var x_expired_checkbox = document.getElementById('expired_checkbox');
         var x_print_full_pull_list = document.getElementById('print_full_btn');
+        var x_print_full_pull_list_alt = document.getElementById('print_alt_btn');
         switch(obj.hold_interface_type) {
             case 'shelf':
                 obj.render_lib_menus({'pickup_lib':true});
@@ -1300,10 +1343,12 @@
                 if (x_lib_type_menu) x_lib_type_menu.hidden = false;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
                 if (x_clear_shelf_widgets) x_clear_shelf_widgets.hidden = false;
+                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
             break;
             case 'pull' :
                 if (x_fetch_more) x_fetch_more.hidden = false;
                 if (x_print_full_pull_list) x_print_full_pull_list.hidden = false;
+                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = false;
                 if (x_lib_type_menu) x_lib_type_menu.hidden = true;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
             break;
@@ -1311,6 +1356,7 @@
                 obj.render_lib_menus({'pickup_lib':true,'request_lib':true});
                 if (x_lib_filter_checkbox) x_lib_filter_checkbox.hidden = false;
                 if (x_lib_type_menu) x_lib_type_menu.hidden = false;
+                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
             break;
             default:
@@ -1319,6 +1365,7 @@
                 if (x_lib_type_menu) x_lib_type_menu.hidden = true;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
                 if (x_show_cancelled_deck) x_show_cancelled_deck.hidden = false;
+                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
             break;
         }
         setTimeout( // We do this because render_lib_menus above creates and appends a DOM node, but until this thread exits, it doesn't really happen
@@ -1346,6 +1393,14 @@
             }, 0
         );
 
+        $('cmd_alt_view').setAttribute('disabled','true');
+        xulG.when_done = function() {
+            $('cmd_alt_view').setAttribute('ready','true');
+            if ($('cmd_alt_view').getAttribute('rendering_rows') != 'true') {
+                $('cmd_alt_view').setAttribute('disabled','false');
+            }
+            dump('hold details UI ready\n');
+        }
         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
         JSAN.use('util.browser');
         obj.browser = new util.browser();
@@ -1355,7 +1410,7 @@
                 'push_xulG' : true,
                 'alt_print' : false,
                 'browser_id' : 'hold_detail_frame',
-                'passthru_content_params' : xulG,
+                'passthru_content_params' : xulG
             }
         );
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/holds_overlay.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,6 +19,7 @@
         <command id="cmd_csv_to_file" />
         <command id="cmd_holds_print" />
         <command id="cmd_holds_print_full" />
+        <command id="cmd_holds_print_alt" />
         <command id="cmd_show_catalog" />
         <command id="cmd_retrieve_patron" />
         <command id="cmd_holds_edit_desire_mint_condition" />
@@ -42,7 +43,7 @@
             accesskey="&staff.circ.holds.title_transfer.accesskey;" />
         <command id="cmd_search_opac" />
         <command id="save_columns" />
-        <command id="cmd_alt_view" />
+        <command id="cmd_alt_view" disabled="true"/>
         <command id="cmd_cancelled_holds_view" />
         <command id="cmd_uncancelled_holds_view" />
         <command id="cmd_view_expired_onshelf_holds"
@@ -188,6 +189,7 @@
 
         <button id="holds_print" label="&staff.patron.holds_overlay.print.label;" command="cmd_holds_print" accesskey="&staff.patron.holds_overlay.print.accesskey;" />
         <button id="print_full_btn" hidden="true" label="&staff.patron.holds_overlay.print_full_pull_list.label;" command="cmd_holds_print_full" accesskey="&staff.patron.holds_overlay.print_full_pull_list.accesskey;" />
+        <button id="print_alt_btn" hidden="true" label="&staff.patron.holds_overlay.print_alt_pull_list.label;" command="cmd_holds_print_alt" accesskey="&staff.patron.holds_overlay.print_alt_pull_list.accesskey;" />
         <spacer flex="1"/>
     </hbox>
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -439,13 +439,19 @@
                     break;
             }
 
+            var horizontal_interface = String( g.data.hash.aous['ui.circ.patron_summary.horizontal'] ) == 'true';
             netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
             var top_xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: auto"><description>' + second_msg + '</description>';
             top_xml += '<hbox><spacer flex="1"/><button label="'+$("patronStrings").getString('staff.patron.info_group.link_patron.move.label')+'"';
             top_xml += ' accesskey="'+$("patronStrings").getString('staff.patron.info_group.link_patron.move.accesskey')+'" name="fancy_submit"/>';
             top_xml += '<button label="'+$("patronStrings").getString('staff.patron.info_group.link_patron.done.label')+'"';
             top_xml += ' accesskey="'+$("patronStrings").getString('staff.patron.info_group.link_patron.done.accesskey')+'" name="fancy_cancel"/></hbox></vbox>';
-            var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: vertical"><hbox flex="1">';
+            var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: vertical">';
+            if (horizontal_interface) {
+                xml += '<vbox flex="1">';
+            } else {
+                xml += '<hbox flex="1">';
+            }
             /************/
             xml += '<vbox flex="1">';
             xml += '<hbox><spacer flex="1"/>';
@@ -470,7 +476,12 @@
             xml += '?show_name=1&amp;id=' + patron_b.id() + '"/>';
             xml += '</vbox>';
             /************/
-            xml += '</hbox></vbox>';
+            if (horizontal_interface) {
+                xml += '</vbox>';
+            } else {
+                xml += '</hbox>';
+            }
+            xml += '</vbox>';
             
             var bot_xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: auto"><hbox>';
             bot_xml += '</hbox></vbox>';

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_group.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,7 +19,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_info_group_win" width="700" height="550" active="true"
-    onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_notes.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_notes.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_notes.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,7 +19,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_info_win" width="700" height="550"
-    onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_stat_cats.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_stat_cats.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_stat_cats.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,7 +19,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_info_stat_cats_win" width="700" height="550"
-    onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_surveys.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_surveys.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/info_surveys.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -19,7 +19,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_info_surveys_win" width="700" height="550"
-    onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -20,7 +20,7 @@
 <?xul-overlay href="/xul/server/patron/items_overlay.xul"?>
 
 <window id="items_win" active="true" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items_overlay.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items_overlay.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/items_overlay.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -99,7 +99,7 @@
 </box>
 
 <vbox id="cmvb1" flex="1">
-    <groupbox id="cmgb1" flex="1">
+    <groupbox id="cmgb1" flex="1" oils_persist="height">
         <caption label="&staff.patron_navbar.items;" />
         <vbox flex="0">
             <hbox id="items_top_ui" />
@@ -110,8 +110,8 @@
             <hbox id="items_bottom_ui" />
         </vbox>
     </groupbox>
-    <splitter><grippy/></splitter>
-    <groupbox flex="1">
+    <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="cmgb1 after_splitter"><grippy/></splitter>
+    <groupbox flex="1" id="after_splitter" oils_persist="height">
         <caption label="&staff.patron_navbar.items.problem_items.caption;" />
         <vbox flex="0">
             <hbox id="items_top_ui2" />

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/new_standing_penalty.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/new_standing_penalty.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/new_standing_penalty.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,8 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="new_penalty_win" 
-    onload="try { new_penalty_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { new_penalty_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+    oils_persist="height width sizemode"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
     title="&staff.patron_display.apply_penalty_dialog.title;">
 

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -20,7 +20,7 @@
 <?xul-overlay href="/xul/server/patron/search_form_overlay.xul"?>
 
 <window id="patron_search_form_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form_horiz.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form_horiz.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_form_horiz.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -20,7 +20,7 @@
 <?xul-overlay href="/xul/server/patron/search_form_horiz_overlay.xul"?>
 
 <window id="patron_search_form_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_result.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_result.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/search_result.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -20,7 +20,7 @@
 <?xul-overlay href="/xul/server/patron/search_result_overlay.xul"?>
 
 <window id="patron_search_result_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/standing_penalties.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/standing_penalties.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/standing_penalties.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -44,7 +44,7 @@
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
     <!-- CONTENT -->
-    <groupbox id="penalty_groupbox" flex="1" class="my_overflow">
+    <groupbox id="penalty_groupbox" flex="1" class="my_overflow" oils_persist="height">
         <caption id="penalty_caption" label="&staff.patron_display.penalty.caption;"/>
         <vbox flex="0">
             <hbox flex="1">
@@ -84,8 +84,8 @@
         </vbox>
         <tree id="ausp_list" flex="1" enableColumnDrag="true" context="ausp_actions" />
     </groupbox>
-    <splitter id="list_splitter" collapse="after" oils_persist="state hidden"><grippy id="splitter_grippy"/></splitter>
-    <groupbox id="archived_penalty_groupbox" flex="1" class="my_overflow">
+    <splitter id="list_splitter" collapse="after" oils_persist="state hidden" oils_persist_peers="penalty_groupbox archived_penalty_groupbox"><grippy id="splitter_grippy"/></splitter>
+    <groupbox id="archived_penalty_groupbox" flex="1" class="my_overflow" oils_persist="height">
         <caption id="penalty_caption" label="&staff.patron_display.archived_penalty.caption;"/>
         <vbox flex="0">
             <toolbox flex="1">

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/summary.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/summary.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/summary.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -20,7 +20,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="patron_summary_win" 
-    onload="try { font_helper(); my_init(); } catch(E) { alert(E); }" onunload="try { observer.unregister(); } catch(E) { alert(E); }"
+    onload="try { font_helper(); my_init(); persist_helper(); } catch(E) { alert(E); }" onunload="try { observer.unregister(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -281,13 +281,26 @@
     }
 }
 
-function uEditResetPw(pw) { 
-    if(!pw) pw = uEditMakeRandomPw(patron);    
-    $('ue_password1').value = pw;
-    $('ue_password2').value = pw;
-    $('ue_password1').onchange();
+function uEditResetPw(pw) {
+    if(!pw) {
+        if(uEditUsePhonePw) {
+            if( (pw = patron.day_phone()) ||
+                (pw = patron.evening_phone()) || (pw = patron.other_phone()) ) {
+                    pw = pw.substring(pw.length - 4); // this is iffy
+                    uEditResetPw(pw);
+                        appendClear($('ue_password_plain'), text(pw));
+                        unHideMe($('ue_password_gen'));
+             }
+        } else {
+            pw = uEditMakeRandomPw(patron);
+        }
+        $('ue_password1').value = pw;
+        $('ue_password2').value = pw;
+        $('ue_password1').onchange();
+    }
 }
 
+
 function uEditClone(clone) {
 
     var cloneUser = fetchFleshedUser(clone);

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue_config.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue_config.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/ue_config.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -295,10 +295,6 @@
                 id            : 'ue_day_phone',
                 type        : 'input',
                 regex        :  phoneRegex,
-                onblur      : function() {
-                    if(uEditUsePhonePw)
-                        uEditMakePhonePw();
-                },
                 onpostchange: function(field, newval) {
                     /*  if this is a new patron and we are using the phone number for
                         the password and the staff edits the phone number after entering
@@ -318,11 +314,7 @@
             widget    : {
                 id            : 'ue_night_phone',
                 type        : 'input',
-                regex        :  phoneRegex,
-                onblur      : function() {
-                    if(uEditUsePhonePw)
-                        uEditMakePhonePw();
-                }
+                regex        :  phoneRegex
             }
         },
         {
@@ -333,11 +325,7 @@
             widget    : {
                 id            : 'ue_other_phone',
                 type        : 'input',
-                regex        :  phoneRegex,
-                onblur      : function() {
-                    if(uEditUsePhonePw)
-                        uEditMakePhonePw();
-                }
+                regex        :  phoneRegex
             }
         },
         {

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/patron/user_buckets.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/patron/user_buckets.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/patron/user_buckets.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -18,7 +18,7 @@
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
 <window id="example_template_win" 
-    onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+    onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -496,7 +496,7 @@
             JSAN.use('util.window'); var win = new util.window();
             win.open(
                 xulG.url_prefix(urls.XUL_SERIAL_SELECT_UNIT),
-                'sel_serial_sunit_win_' + win.window_name_increment(),
+                '_blank',
                 'chrome,resizable,modal,centerscreen'
             );
             if (!g.serial_items_sunit_select) {

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -82,9 +82,9 @@
                 </menu>
             </menubar>
         </hbox>
-        <tree id="item_tree" flex="2" enableColumnDrag="true" context="serial_manage_items_popup"/>
-        <splitter state="open" collapse="after" resizebefore="closest" resizeafter="farthest"/>
-        <hbox align="center">
+        <tree id="item_tree" flex="2" enableColumnDrag="true" context="serial_manage_items_popup" oils_persist="height"/>
+        <splitter state="open" collapse="after" resizebefore="closest" resizeafter="farthest" id="splitter" oils_persist="state hidden" oils_persist_peers="item_tree after_splitter"/>
+        <hbox align="center" id="after_splitter" oils_persist="height">
             <label style="font-weight: bold" value="Showing: "/>
             <label id="serial_workarea_mode_label" value="Recently Received"/>
             <spacer flex="1"/>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -65,7 +65,7 @@
                 </popupset>
 
                 <hbox flex="1">
-                    <vbox flex="1">
+                    <vbox flex="1" id="before_splitter" oils_persist="width">
                         <hbox id="serial_sub_lib_menu"/>
                         <hbox>
                             <checkbox id="show_ssubs" label="Show Subs." />
@@ -92,8 +92,8 @@
                         </hbox>
                         <tree id="subs_tree" flex="15" enableColumnDrag="true" context="serial_manage_subs_popup"/>
                     </vbox>
-                    <splitter state="open" collapse="before" resizebefore="closest" resizeafter="farthest"/>
-                    <deck id="serial_manage_subs_editor_deck" flex="20">
+                    <splitter state="open" collapse="before" resizebefore="closest" resizeafter="farthest" id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter serial_manage_subs_editor_deck"/>
+                    <deck id="serial_manage_subs_editor_deck" flex="20" oils_persist="width">
                         <description value="Please select an object to edit"/>
                         <vbox id="serial_ssub_editor_panel" />
                         <vbox id="serial_sdist_editor_panel" />

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/notes.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/notes.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/notes.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -22,8 +22,8 @@
 <!-- OVERLAYS -->
 <?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
 
-<window id="notes_win" width="700" height="550"
-	onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="notes_win" width="700" height="550" oils_persist="height width sizemode"
+	onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
 	xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
 	<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sdist_editor.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sdist_editor.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sdist_editor.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -14,12 +14,12 @@
         <vbox id="brief_display_box"/>
 
 		<hbox flex="1" style="overflow: auto">
-			<vbox flex="1">
+			<vbox flex="1" id="before_splitter" oils_persist="height">
 				<label value="Distribution" style="font-weight: bold; font-size: large"/>
 				<vbox id="sdist_editor_left_pane" flex="1"/>
 			</vbox>
-			<splitter><grippy /></splitter>
-			<vbox flex="1">
+			<splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy /></splitter>
+			<vbox flex="1" id="after_splitter" oils_persist="height">
 				<vbox id="sdist_editor_right_pane"/>
                 <groupbox>
                     <caption label="Library Specific Options" />

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -25,7 +25,7 @@
 <?xul-overlay href="/xul/server/serial/manage_subs.xul"?>
 
 <window id="serial_serctrl_main" 
-	onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+	onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
 	xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
 	<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/siss_editor.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/siss_editor.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/siss_editor.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -14,16 +14,16 @@
         <vbox id="brief_display_box"/>
 
 		<hbox flex="1" style="overflow: auto">
-			<vbox flex="1">
+			<vbox flex="1" id="before_splitter1" oils_persist="width">
 				<label value="Issuance" style="font-weight: bold; font-size: large"/>
 				<vbox id="siss_editor_left_pane" flex="1"/>
 			</vbox>
-			<splitter><grippy /></splitter>
-			<vbox flex="1">
+			<splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1"><grippy /></splitter>
+			<vbox flex="1" id="after_splitter1" oils_persist="width">
 				<vbox id="siss_editor_middle_pane"/>
 			</vbox>
-			<splitter><grippy /></splitter>
-			<vbox flex="1">
+			<splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_splitter1 after_splitter2"><grippy /></splitter>
+			<vbox flex="1" id="after_splitter2" oils_persist="width">
 				<vbox id="siss_editor_right_pane"/>
 			</vbox>
 		</hbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sitem_editor.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sitem_editor.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/sitem_editor.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -66,17 +66,17 @@
 
 	<groupbox flex="1" class="my_overflow">
 		<hbox flex="1" style="overflow: auto">
-			<vbox flex="1">
+			<vbox flex="1" id="before_splitter1" oils_persist="width">
 				<label value="Item" style="font-weight: bold; font-size: large"/>
 				<vbox id="sitem_editor_left_pane" flex="1"/>
 			</vbox>
-			<splitter><grippy /></splitter>
-			<vbox flex="1">
+			<splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1"><grippy /></splitter>
+			<vbox flex="1" id="after_splitter1" oils_persist="width">
 				<label value=" " style="font-weight: bold; font-size: large"/>
 				<vbox id="sitem_editor_middle_pane" flex="1"/>
 			</vbox>
-			<splitter><grippy /></splitter>
-			<vbox flex="1">
+			<splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_slitter1 after_splitter2"><grippy /></splitter>
+			<vbox flex="1" id="after_splitter2" oils_persist="width">
 				<button style="font-weight: bold; font-size: normal" label="Item Dates" accesskey="1" oncommand="document.getElementById('sitem_editor_right_pane').firstChild.firstChild.focus();"/>
 				<vbox id="sitem_editor_right_pane" flex="1"/>
 			</vbox>

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/serial/ssub_editor.xul
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/serial/ssub_editor.xul	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/serial/ssub_editor.xul	2010-10-18 14:05:06 UTC (rev 18372)
@@ -14,12 +14,12 @@
         <vbox id="brief_display_box"/>
 
 		<hbox flex="1" style="overflow: auto">
-			<vbox flex="1">
+			<vbox flex="1" id="before_splitter" oils_persist="width">
 				<label value="Subscription" style="font-weight: bold; font-size: large"/>
 				<vbox id="left_pane" flex="1"/>
 			</vbox>
-			<splitter><grippy /></splitter>
-			<vbox flex="1">
+			<splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy /></splitter>
+			<vbox flex="1" id="after_splitter" oils_persist="width">
 				<label value=" " style="font-weight: bold; font-size: large"/>
 				<vbox id="right_pane" flex="1"/>
 			</vbox>

Copied: branches/serials-integration/Open-ILS/xul/staff_client/server/skin/custom.js (from rev 18366, trunk/Open-ILS/xul/staff_client/server/skin/custom.js)
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/skin/custom.js	                        (rev 0)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/skin/custom.js	2010-10-18 14:05:06 UTC (rev 18372)
@@ -0,0 +1,9 @@
+/* Settings here override default values from constants.js;for example:
+ 
+    urls['AUDIO_good'] = '/xul/server/skin/media/custom/good.wav';
+    urls['opac'] = '/opac/' + LOCALE + '/skin/mylib/xml/advanced.xml?nps=1';
+    urls['opac_rdetail'] = '/opac/' + LOCALE + '/skin/mylib/xml/rdetail.xml';
+    urls['opac_rresult'] = '/opac/' + LOCALE + '/skin/mylib/xml/rresult.xml';
+    urls['browser'] = '/opac/' + LOCALE + '/skin/mylib/xml/advanced.xml?nps=1';
+
+*/

Modified: branches/serials-integration/Open-ILS/xul/staff_client/server/skin/patron_display.css
===================================================================
--- branches/serials-integration/Open-ILS/xul/staff_client/server/skin/patron_display.css	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/Open-ILS/xul/staff_client/server/skin/patron_display.css	2010-10-18 14:05:06 UTC (rev 18372)
@@ -50,9 +50,6 @@
 .PATRON_EXCEEDS_FINES label.max_bills_indicator { display: inline; color: purple; }
 .PATRON_EXCEEDS_FINES label#under_bills { color: red; }
 
-.PATRON_AGE_LT_18 .dob { color: purple; }
-.PATRON_AGE_LT_18 label.juvenile_indicator { display: inline; color: purple; }
-
 .PATRON_HAS_INVALID_DOB .dob { color: purple; }
 .PATRON_HAS_INVALID_DOB label.invalid_dob_indicator { display: inline; color: purple }
 

Modified: branches/serials-integration/build/tools/update.sh
===================================================================
--- branches/serials-integration/build/tools/update.sh	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/build/tools/update.sh	2010-10-18 14:05:06 UTC (rev 18372)
@@ -58,10 +58,11 @@
 and error-prone tasks associated with an upgrade for a developer.
 
 Considerations:
- * Run as opensrf.  
+ * Run as opensrf user
  * opensrf needs sudo 
- * Assumes opensrf has a configured (as in ./configure) both ILS and 
-   OpenSRF as svn or git-svn checkouts
+ * Assumes opensrf has OpenILS and OpenSRF repositories as svn or git-svn 
+   checkouts and both have been configured (as in ./configure) 
+  
 END_OF_USAGE
 }
 
@@ -98,6 +99,7 @@
 OSRF=${OPT_OSRFDIR:-$BASE/OpenSRF/trunk};
 ILS=${OPT_EGDIR:-$(pwd)};
 XUL="$INSTALL/var/web/xul";
+JSDIR="$INSTALL/lib/javascript";    # only used for FULL install
 
 # ----------------------------------
 # TEST and SANITY CHECK
@@ -150,6 +152,10 @@
     cd $OSRF && make;
     cd $ILS  && make;
     cd $OSRF && sudo make install;
+    if [ -d "$JSDIR" ]; then
+        echo "Copying OpenSRF javascript files into $JSDIR";
+        cp ./src/javascript/* $JSDIR;
+    fi
 fi
 sudo chown -R opensrf:opensrf $INSTALL
 

Modified: branches/serials-integration/build/tools/update_db.sh
===================================================================
--- branches/serials-integration/build/tools/update_db.sh	2010-10-18 13:46:56 UTC (rev 18371)
+++ branches/serials-integration/build/tools/update_db.sh	2010-10-18 14:05:06 UTC (rev 18372)
@@ -60,21 +60,36 @@
 # [ $VERBOSE ] && echo RAW VERSION: $VERSION     # TODO: for verbose mode
 VERSION=$(echo $VERSION | sed -e 's/^ *0*//');    # This is a separate step so we can check $? above.
 [ -z "$VERSION" ] && usage_die "config.upgrade_log missing ANY installed version data!";
-echo "* Last installed version -> $VERSION";
+echo "* Last installed version  ->  $VERSION";
 
 if [ -d ./Open-ILS/src/sql/Pg ] ; then
     cd ./Open-ILS/src/sql/Pg ;
 fi
 [ -d ./upgrade ] || usage_die "No ./upgrade directory found.  Please run from Open-ILS/src/sql/Pg";
 
+MAX=$(ls upgrade/[0-9][0-9][0-9][0-9]* 2>/dev/null | tail -1 | cut -c9-12 );   # could take an optional arg to set this, if we wanted.
+echo "* Last upgrade file found -> $MAX";
+MAX=$(echo $MAX | sed -e 's/^ *0*//');      # remove leading zeroes
+
 declare -a FILES;
+declare -a SKIPPED;
 while true; do
     VERSION=$(($VERSION + 1));
+    [ $VERSION -gt $MAX ] && break;
     PREFIX=$(printf "%0.4d" $VERSION);
     FILE=$(ls upgrade/$PREFIX* 2>/dev/null);
-    [ ! -f "$FILE" ] && break;
-    FILES[${#FILES[@]}]=$FILE;      # "push" onto FILES array
-    echo "* Pending $FILE";
+    if [ -f "$FILE" ] ; then
+        # Note: we only report skipped files once we find the next one.  
+        # Otherwise, we'd report everything from $VERSION+1 to $MAX
+        for skip in ${SKIPPED[@]} ; do
+            echo "* WARNING: Upgrade $skip NOT FOUND.  Skipping it."; 
+        done
+        SKIPPED=();                     # After we reported them, reset array.
+        FILES[${#FILES[@]}]=$FILE;      # "push" onto FILES array
+        # echo "* Pending $FILE";
+    else
+        SKIPPED[${#SKIPPED[@]}]=$PREFIX; # "push" onto SKIPPED array
+    fi
 done;
 
 COUNT=${#FILES[@]};



More information about the open-ils-commits mailing list