Merge branch 'master' of ssh://yeti.esilibrary.com/home/evergreen/evergreen-equinox...
authorsenator <lebbeous@esilibrary.com>
Wed, 16 Mar 2011 18:50:14 +0000 (14:50 -0400)
committersenator <lebbeous@esilibrary.com>
Wed, 16 Mar 2011 18:50:14 +0000 (14:50 -0400)
29 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
Open-ILS/src/sql/Pg/000.english.pg90.fts-config.sql
Open-ILS/src/sql/Pg/000.functions.general.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/002.functions.aggregate.sql
Open-ILS/src/sql/Pg/002.functions.general.sql [deleted file]
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/2.0.3-2.0.4-upgrade-db.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/build-db.sh
Open-ILS/src/sql/Pg/reingest-1.6-2.0.pl
Open-ILS/src/sql/Pg/upgrade/0499.schema.generic_CN_normalizer.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0500.schema.search_path_mangling.sql [new file with mode: 0644]
Open-ILS/web/js/dojo/fieldmapper/OrgUtils.js
Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/web/opac/skin/default/js/holds.js
Open-ILS/web/reports/oils_rpt_folders.js
Open-ILS/web/templates/default/conify/global/config/record_attr_definition.tt2
Open-ILS/xul/staff_client/Makefile.am
Open-ILS/xul/staff_client/chrome/content/main/menu.js
Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
Open-ILS/xul/staff_client/external/aa_per_machine.js [new file with mode: 0644]
Open-ILS/xul/staff_client/external/make_updates.sh
Open-ILS/xul/staff_client/extras.example.nsi [new file with mode: 0644]
Open-ILS/xul/staff_client/server/circ/renew.js
Open-ILS/xul/staff_client/server/main/ws_info.xul
Open-ILS/xul/staff_client/windowssetup.nsi

index 8c39296..3047e2a 100644 (file)
@@ -1684,7 +1684,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
        </class>
        <class id="cxt" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::xml_transform" oils_persist:tablename="config.xml_transform" reporter:label="XML/XSLT Transform Definition">
                <fields oils_persist:primary="name">
-                       <field reporter:label="Field Class" name="field_class" />
                        <field reporter:label="Name" name="name" reporter:datatype="text" />
                        <field reporter:label="Namespace URI" name="namespace_uri" reporter:datatype="text"/>
                        <field reporter:label="Namespace Prefix" name="prefix" reporter:datatype="text"/>
index a88cdac..460244a 100644 (file)
@@ -520,8 +520,12 @@ sub format_payment_receipt {
 
     my $for_print = ($self->api_name =~ /print/);
     my $for_email = ($self->api_name =~ /email/);
-    my $e = new_editor(authtoken => $auth);
-    return $e->event unless $e->checkauth;
+
+    # manually use xact (i.e. authoritative) so we can kill the cstore
+    # connection before sending the action/trigger request.  This prevents our cstore
+    # backend from sitting idle while A/T (which uses its own transactions) runs.
+    my $e = new_editor(xact => 1, authtoken => $auth);
+    return $e->die_event unless $e->checkauth;
 
     my $payments = [];
     for my $id (@$mp_ids) {
@@ -534,15 +538,17 @@ sub format_payment_receipt {
                     mbt => ['usr']
                 }
             }
-        ]) or return $e->event;
+        ]) or return $e->die_event;
 
-        return $e->event unless 
+        return $e->die_event unless 
             $e->requestor->id == $payment->xact->usr->id or
             $e->allowed('VIEW_TRANSACTION', $payment->xact->usr->home_ou); 
 
         push @$payments, $payment;
     }
 
+    $e->rollback;
+
     if ($for_print) {
 
         return $U->fire_object_event(undef, 'money.format.payment_receipt.print', $payments, $$payments[0]->xact->usr->home_ou);
index cc9801e..e8aee2f 100644 (file)
@@ -1263,7 +1263,7 @@ sub new_hold_copy_targeter {
                                                isTrue($_->location->holdable) && 
                                                isTrue($_->holdable) &&
                                                !isTrue($_->deleted) &&
-                                               isTrue($hold->mint_condition) ? isTrue($_->mint_condition) : 1
+                                               (isTrue($hold->mint_condition) ? isTrue($_->mint_condition) : 1)
                                        } @$all_copies;
 
                        # let 'em know we're still working
index e4e50e4..7ddce06 100644 (file)
@@ -18,6 +18,8 @@
 
 BEGIN;
 
+SET search_path = public, pg_catalog;
+
 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
 BEGIN
        NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, NEW.value);
diff --git a/Open-ILS/src/sql/Pg/000.functions.general.sql b/Open-ILS/src/sql/Pg/000.functions.general.sql
new file mode 100644 (file)
index 0000000..f960341
--- /dev/null
@@ -0,0 +1,22 @@
+-- Rather than polluting the public schema with general Evergreen
+-- functions, carve out a dedicated schema
+
+DROP SCHEMA IF EXISTS evergreen CASCADE;
+
+BEGIN;
+
+CREATE SCHEMA evergreen;
+
+CREATE OR REPLACE FUNCTION evergreen.lowercase( TEXT ) RETURNS TEXT AS $$
+    return lc(shift);
+$$ LANGUAGE PLPERLU STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION evergreen.change_db_setting(setting_name TEXT, settings TEXT[]) RETURNS VOID AS $$
+BEGIN
+EXECUTE 'ALTER DATABASE ' || quote_ident(current_database()) || ' SET ' || quote_ident(setting_name) || ' = ' || array_to_string(settings, ',');
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT evergreen.change_db_setting('search_path', ARRAY['public','pg_catalog']);
+
+COMMIT;
index fb0c311..bf4fcfa 100644 (file)
@@ -21,7 +21,7 @@ DROP AGGREGATE IF EXISTS array_accum(anyelement) CASCADE;
 DROP AGGREGATE IF EXISTS public.first(anyelement) CASCADE;
 DROP AGGREGATE IF EXISTS public.last(anyelement) CASCADE;
 DROP AGGREGATE IF EXISTS public.agg_text(text) CASCADE;
-DROP AGGREGATE IF EXISTS public.agg_tsvector(tsvector) CASCADE;
+DROP AGGREGATE IF EXISTS public.agg_tsvector(pg_catalog.tsvector) CASCADE;
 
 CREATE AGGREGATE array_accum (
        sfunc = array_append,
@@ -66,7 +66,7 @@ CREATE AGGREGATE public.agg_text (
        stype    = text
 );
 
-CREATE OR REPLACE FUNCTION public.tsvector_concat ( tsvector, tsvector ) RETURNS tsvector AS $$
+CREATE OR REPLACE FUNCTION tsvector_concat ( tsvector, tsvector ) RETURNS pg_catalog.tsvector AS $$
 SELECT
        CASE    WHEN $1 IS NULL
                        THEN $2
@@ -77,9 +77,9 @@ SELECT
 $$ LANGUAGE SQL STABLE;
 
 CREATE AGGREGATE public.agg_tsvector (
-       sfunc    = public.tsvector_concat,
-       basetype = tsvector,
-       stype    = tsvector
+       sfunc    = tsvector_concat,
+       basetype = pg_catalog.tsvector,
+       stype    = pg_catalog.tsvector
 );
 
 CREATE OR REPLACE FUNCTION public.explode_array(anyarray) RETURNS SETOF anyelement AS $BODY$
diff --git a/Open-ILS/src/sql/Pg/002.functions.general.sql b/Open-ILS/src/sql/Pg/002.functions.general.sql
deleted file mode 100644 (file)
index f9407a5..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
--- Rather than polluting the public schema with general Evergreen
--- functions, carve out a dedicated schema
-
-DROP SCHEMA IF EXISTS evergreen CASCADE;
-
-BEGIN;
-
-CREATE SCHEMA evergreen;
-
-CREATE OR REPLACE FUNCTION evergreen.lowercase( TEXT ) RETURNS TEXT AS $$
-    return lc(shift);
-$$ LANGUAGE PLPERLU STRICT IMMUTABLE;
-
-COMMIT;
index 018d806..b393ef3 100644 (file)
@@ -70,7 +70,7 @@ CREATE TABLE config.upgrade_log (
     install_date    TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
 );
 
-INSERT INTO config.upgrade_log (version) VALUES ('0498'); -- dbs
+INSERT INTO config.upgrade_log (version) VALUES ('0500'); -- miker for dbs
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
index 18357ef..d2a8204 100644 (file)
@@ -207,6 +207,7 @@ CREATE OR REPLACE FUNCTION asset.label_normalizer_generic(TEXT) RETURNS TEXT AS
     # thus could probably be considered a derived work, although nothing was
     # directly copied - but to err on the safe side of providing attribution:
     # Copyright (C) 2007 LibLime
+    # Copyright (C) 2011 Equinox Software, Inc (Steve Callendar)
     # Licensed under the GPL v2 or later
 
     use strict;
@@ -214,13 +215,16 @@ CREATE OR REPLACE FUNCTION asset.label_normalizer_generic(TEXT) RETURNS TEXT AS
 
     # Converts the callnumber to uppercase
     # Strips spaces from start and end of the call number
-    # Converts anything other than letters, digits, and periods into underscores
-    # Collapses multiple underscores into a single underscore
+    # Converts anything other than letters, digits, and periods into spaces
+    # Collapses multiple spaces into a single underscore
     my $callnum = uc(shift);
     $callnum =~ s/^\s//g;
     $callnum =~ s/\s$//g;
-    $callnum =~ s/[^A-Z0-9_.]/_/g;
-    $callnum =~ s/_{2,}/_/g;
+    # NOTE: this previously used underscores, but this caused sorting issues
+    # for the "before" half of page 0 on CN browse, sorting CNs containing a
+    # decimal before "whole number" CNs
+    $callnum =~ s/[^A-Z0-9_.]/ /g;
+    $callnum =~ s/ {2,}/ /g;
 
     return $callnum;
 $func$ LANGUAGE PLPERLU;
diff --git a/Open-ILS/src/sql/Pg/2.0.3-2.0.4-upgrade-db.sql b/Open-ILS/src/sql/Pg/2.0.3-2.0.4-upgrade-db.sql
new file mode 100644 (file)
index 0000000..8726a21
--- /dev/null
@@ -0,0 +1,164 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0498');
+
+-- Rather than polluting the public schema with general Evergreen
+-- functions, carve out a dedicated schema
+CREATE SCHEMA evergreen;
+
+-- Replace all uses of PostgreSQL's built-in LOWER() function with
+-- a more locale-savvy PLPERLU evergreen.lowercase() function
+CREATE OR REPLACE FUNCTION evergreen.lowercase( TEXT ) RETURNS TEXT AS $$
+    return lc(shift);
+$$ LANGUAGE PLPERLU STRICT IMMUTABLE;
+
+-- update actor.usr_address indexes
+DROP INDEX IF EXISTS actor.actor_usr_addr_street1_idx;
+DROP INDEX IF EXISTS actor.actor_usr_addr_street2_idx;
+DROP INDEX IF EXISTS actor.actor_usr_addr_city_idx;
+DROP INDEX IF EXISTS actor.actor_usr_addr_state_idx; 
+DROP INDEX IF EXISTS actor.actor_usr_addr_post_code_idx;
+
+CREATE INDEX actor_usr_addr_street1_idx ON actor.usr_address (evergreen.lowercase(street1));
+CREATE INDEX actor_usr_addr_street2_idx ON actor.usr_address (evergreen.lowercase(street2));
+CREATE INDEX actor_usr_addr_city_idx ON actor.usr_address (evergreen.lowercase(city));
+CREATE INDEX actor_usr_addr_state_idx ON actor.usr_address (evergreen.lowercase(state));
+CREATE INDEX actor_usr_addr_post_code_idx ON actor.usr_address (evergreen.lowercase(post_code));
+
+-- update actor.usr indexes
+DROP INDEX IF EXISTS actor.actor_usr_first_given_name_idx;
+DROP INDEX IF EXISTS actor.actor_usr_second_given_name_idx;
+DROP INDEX IF EXISTS actor.actor_usr_family_name_idx;
+DROP INDEX IF EXISTS actor.actor_usr_email_idx;
+DROP INDEX IF EXISTS actor.actor_usr_day_phone_idx;
+DROP INDEX IF EXISTS actor.actor_usr_evening_phone_idx;
+DROP INDEX IF EXISTS actor.actor_usr_other_phone_idx;
+DROP INDEX IF EXISTS actor.actor_usr_ident_value_idx;
+DROP INDEX IF EXISTS actor.actor_usr_ident_value2_idx;
+
+CREATE INDEX actor_usr_first_given_name_idx ON actor.usr (evergreen.lowercase(first_given_name));
+CREATE INDEX actor_usr_second_given_name_idx ON actor.usr (evergreen.lowercase(second_given_name));
+CREATE INDEX actor_usr_family_name_idx ON actor.usr (evergreen.lowercase(family_name));
+CREATE INDEX actor_usr_email_idx ON actor.usr (evergreen.lowercase(email));
+CREATE INDEX actor_usr_day_phone_idx ON actor.usr (evergreen.lowercase(day_phone));
+CREATE INDEX actor_usr_evening_phone_idx ON actor.usr (evergreen.lowercase(evening_phone));
+CREATE INDEX actor_usr_other_phone_idx ON actor.usr (evergreen.lowercase(other_phone));
+CREATE INDEX actor_usr_ident_value_idx ON actor.usr (evergreen.lowercase(ident_value));
+CREATE INDEX actor_usr_ident_value2_idx ON actor.usr (evergreen.lowercase(ident_value2));
+
+-- update actor.card indexes
+DROP INDEX IF EXISTS actor.actor_card_barcode_evergreen_lowercase_idx;
+CREATE INDEX actor_card_barcode_evergreen_lowercase_idx ON actor.card (evergreen.lowercase(barcode));
+
+CREATE OR REPLACE FUNCTION vandelay.match_bib_record ( ) RETURNS TRIGGER AS $func$
+DECLARE
+    attr        RECORD;
+    attr_def    RECORD;
+    eg_rec      RECORD;
+    id_value    TEXT;
+    exact_id    BIGINT;
+BEGIN
+
+    DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
+
+    SELECT * INTO attr_def FROM vandelay.bib_attr_definition WHERE xpath = '//*[@tag="901"]/*[@code="c"]' ORDER BY id LIMIT 1;
+
+    IF attr_def IS NOT NULL AND attr_def.id IS NOT NULL THEN
+        id_value := extract_marc_field('vandelay.queued_bib_record', NEW.id, attr_def.xpath, attr_def.remove);
+    
+        IF id_value IS NOT NULL AND id_value <> '' AND id_value ~ $r$^\d+$$r$ THEN
+            SELECT id INTO exact_id FROM biblio.record_entry WHERE id = id_value::BIGINT AND NOT deleted;
+            SELECT * INTO attr FROM vandelay.queued_bib_record_attr WHERE record = NEW.id and field = attr_def.id LIMIT 1;
+            IF exact_id IS NOT NULL THEN
+                INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, exact_id);
+            END IF;
+        END IF;
+    END IF;
+
+    IF exact_id IS NULL THEN
+        FOR attr IN SELECT a.* FROM vandelay.queued_bib_record_attr a JOIN vandelay.bib_attr_definition d ON (d.id = a.field) WHERE record = NEW.id AND d.ident IS TRUE LOOP
+    
+               -- All numbers? check for an id match
+               IF (attr.attr_value ~ $r$^\d+$$r$) THEN
+               FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE id = attr.attr_value::BIGINT AND deleted IS FALSE LOOP
+                       INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, eg_rec.id);
+                       END LOOP;
+               END IF;
+    
+               -- Looks like an ISBN? check for an isbn match
+               IF (attr.attr_value ~* $r$^[0-9x]+$$r$ AND character_length(attr.attr_value) IN (10,13)) THEN
+               FOR eg_rec IN EXECUTE $$SELECT * FROM metabib.full_rec fr WHERE fr.value LIKE evergreen.lowercase('$$ || attr.attr_value || $$%') AND fr.tag = '020' AND fr.subfield = 'a'$$ LOOP
+                               PERFORM id FROM biblio.record_entry WHERE id = eg_rec.record AND deleted IS FALSE;
+                               IF FOUND THEN
+                               INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('isbn', attr.id, NEW.id, eg_rec.record);
+                               END IF;
+                       END LOOP;
+    
+                       -- subcheck for isbn-as-tcn
+                   FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = 'i' || attr.attr_value AND deleted IS FALSE LOOP
+                           INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
+               END LOOP;
+               END IF;
+    
+               -- check for an OCLC tcn_value match
+               IF (attr.attr_value ~ $r$^o\d+$$r$) THEN
+                   FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = regexp_replace(attr.attr_value,'^o','ocm') AND deleted IS FALSE LOOP
+                           INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
+               END LOOP;
+               END IF;
+    
+               -- check for a direct tcn_value match
+            FOR eg_rec IN SELECT * FROM biblio.record_entry WHERE tcn_value = attr.attr_value AND deleted IS FALSE LOOP
+                INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('tcn_value', attr.id, NEW.id, eg_rec.id);
+            END LOOP;
+    
+               -- check for a direct item barcode match
+            FOR eg_rec IN
+                    SELECT  DISTINCT b.*
+                      FROM  biblio.record_entry b
+                            JOIN asset.call_number cn ON (cn.record = b.id)
+                            JOIN asset.copy cp ON (cp.call_number = cn.id)
+                      WHERE cp.barcode = attr.attr_value AND cp.deleted IS FALSE
+            LOOP
+                INSERT INTO vandelay.bib_match (field_type, matched_attr, queued_record, eg_record) VALUES ('id', attr.id, NEW.id, eg_rec.id);
+            END LOOP;
+    
+        END LOOP;
+    END IF;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0499');
+
+CREATE OR REPLACE FUNCTION asset.label_normalizer_generic(TEXT) RETURNS TEXT AS $func$
+    # Created after looking at the Koha C4::ClassSortRoutine::Generic module,
+    # thus could probably be considered a derived work, although nothing was
+    # directly copied - but to err on the safe side of providing attribution:
+    # Copyright (C) 2007 LibLime
+    # Copyright (C) 2011 Equinox Software, Inc (Steve Callendar)
+    # Licensed under the GPL v2 or later
+
+    use strict;
+    use warnings;
+
+    # Converts the callnumber to uppercase
+    # Strips spaces from start and end of the call number
+    # Converts anything other than letters, digits, and periods into spaces
+    # Collapses multiple spaces into a single underscore
+    my $callnum = uc(shift);
+    $callnum =~ s/^\s//g;
+    $callnum =~ s/\s$//g;
+    # NOTE: this previously used underscores, but this caused sorting issues
+    # for the "before" half of page 0 on CN browse, sorting CNs containing a
+    # decimal before "whole number" CNs
+    $callnum =~ s/[^A-Z0-9_.]/ /g;
+    $callnum =~ s/ {2,}/ /g;
+
+    return $callnum;
+$func$ LANGUAGE PLPERLU;
+
+UPDATE asset.call_number SET id = id;
+
+COMMIT;
index 0e8f6f8..ef9b7b9 100755 (executable)
@@ -79,11 +79,12 @@ fi
 # This describes the order in which the SQL files will be eval'd by psql.
 # ---------------------------------------------------------------------------
 ordered_file_list="
+  000.functions.general.sql
+
   $fts_config_file
 
   001.schema.offline.sql
 
-  002.functions.general.sql
   002.schema.config.sql
   002.functions.aggregate.sql
   002.functions.config.sql
index 988a7b0..483cc92 100755 (executable)
@@ -77,6 +77,21 @@ sub fetch_num_bibs_from_database {
 
 sub header {
     print OUT q {
+\qecho First, make sure that the rows needed for title sorting are
+\qecho available.
+
+BEGIN; 
+DELETE FROM metabib.real_full_rec WHERE tag = 'tnf'; 
+INSERT INTO metabib.real_full_rec (record, tag, subfield, value) 
+   SELECT  record, 
+           'tnf', 
+           'a', 
+           SUBSTRING(value, COALESCE(NULLIF(REGEXP_REPLACE(ind2,'[^0-9]','','g'),''),'0')::int + 1) 
+     FROM  metabib.real_full_rec 
+     WHERE tag = '245' 
+           AND subfield = 'a'; 
+COMMIT;
+
 \qecho Do a partial reingest to fully populate metabib.facet_entry
 \qecho and update the keyword indexes to reflect changes in the default
 \qecho NACO normalization.  This can be time consuming on large databases.
diff --git a/Open-ILS/src/sql/Pg/upgrade/0499.schema.generic_CN_normalizer.sql b/Open-ILS/src/sql/Pg/upgrade/0499.schema.generic_CN_normalizer.sql
new file mode 100644 (file)
index 0000000..7fdf72a
--- /dev/null
@@ -0,0 +1,35 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0499'); -- miker for Steve Callendar
+
+CREATE OR REPLACE FUNCTION asset.label_normalizer_generic(TEXT) RETURNS TEXT AS $func$
+    # Created after looking at the Koha C4::ClassSortRoutine::Generic module,
+    # thus could probably be considered a derived work, although nothing was
+    # directly copied - but to err on the safe side of providing attribution:
+    # Copyright (C) 2007 LibLime
+    # Copyright (C) 2011 Equinox Software, Inc (Steve Callendar)
+    # Licensed under the GPL v2 or later
+
+    use strict;
+    use warnings;
+
+    # Converts the callnumber to uppercase
+    # Strips spaces from start and end of the call number
+    # Converts anything other than letters, digits, and periods into spaces
+    # Collapses multiple spaces into a single underscore
+    my $callnum = uc(shift);
+    $callnum =~ s/^\s//g;
+    $callnum =~ s/\s$//g;
+    # NOTE: this previously used underscores, but this caused sorting issues
+    # for the "before" half of page 0 on CN browse, sorting CNs containing a
+    # decimal before "whole number" CNs
+    $callnum =~ s/[^A-Z0-9_.]/ /g;
+    $callnum =~ s/ {2,}/ /g;
+
+    return $callnum;
+$func$ LANGUAGE PLPERLU;
+
+UPDATE asset.call_number SET id = id;
+
+COMMIT;
+
diff --git a/Open-ILS/src/sql/Pg/upgrade/0500.schema.search_path_mangling.sql b/Open-ILS/src/sql/Pg/upgrade/0500.schema.search_path_mangling.sql
new file mode 100644 (file)
index 0000000..9068a2e
--- /dev/null
@@ -0,0 +1,13 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0500');
+
+CREATE OR REPLACE FUNCTION evergreen.change_db_setting(setting_name TEXT, settings TEXT[]) RETURNS VOID AS $$
+BEGIN
+EXECUTE 'ALTER DATABASE ' || quote_ident(current_database()) || ' SET ' || quote_ident(setting_name) || ' = ' || array_to_string(settings, ',');
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT evergreen.change_db_setting('search_path', ARRAY['public','pg_catalog']);
+
+COMMIT;
index f621fab..8a1000c 100644 (file)
@@ -46,7 +46,7 @@ if(!dojo._hasResource["fieldmapper.OrgUtils"]){
     };
 
     fieldmapper.aou.LoadOrg = function (id, slim_ok) {
-        if (slim_ok === null) slim_ok = fieldmapper.aou.slim_ok;
+        if (slim_ok == null) slim_ok = fieldmapper.aou.slim_ok;
         var slim_o = fieldmapper.aou.OrgCache[id];
 
         if (slim_o && (slim_ok || slim_o.loaded))
@@ -80,7 +80,7 @@ if(!dojo._hasResource["fieldmapper.OrgUtils"]){
 
         for (var i in fieldmapper.aou.OrgCache) {
             var x = fieldmapper.aou.OrgCache[i].org;
-            if (x.parent_ou() === null || x.parent_ou() === '') {
+            if (x.parent_ou() == null || x.parent_ou() == '') {
                 fieldmapper.aou.globalOrgTree = x;
                 continue;
             }
@@ -135,7 +135,7 @@ if(!dojo._hasResource["fieldmapper.OrgUtils"]){
     fieldmapper.aou.findOrgDepth = fieldmapper.aou.prototype.findOrgDepth;
 
     fieldmapper.aout.findOrgTypeFromDepth = function (depth) {
-        if( depth === null ) return null;
+        if( depth == null ) return null;
         fieldmapper.aout.LoadOrgTypes();
         for( var i in fieldmapper.aout.OrgTypeCache ) {
             var t = fieldmapper.aout.OrgTypeCache[i].type;
@@ -145,7 +145,7 @@ if(!dojo._hasResource["fieldmapper.OrgUtils"]){
     };
 
     fieldmapper.aou.findOrgUnitSN = function (sn, slim_ok) {
-        if (slim_ok === null) slim_ok = fieldmapper.aou.slim_ok;
+        if (slim_ok == null) slim_ok = fieldmapper.aou.slim_ok;
         var org = fieldmapper.aou.OrgCacheSN[sn];
         if (!org) {
             for (var i in fieldmapper.aou.OrgCache) {
index 3fa7735..e171f30 100644 (file)
@@ -583,7 +583,8 @@ if(!dojo._hasResource['openils.widget.AutoFieldWidget']) {
                 // invalid data.  This change tells dojo to pretend this focusing has 
                 // already happened so we can style required widgets during page render.
                 this.widget._hasBeenBlurred = true;
-                this.widget.validate();
+                if(this.widget.validate)
+                    this.widget.validate();
             }
         },
 
index d22cfd1..f9fcd6e 100644 (file)
 <!ENTITY staff.main.menu.admin.server_admin.conify.grp_tree.label "Permission Groups">
 <!ENTITY staff.main.menu.admin.server_admin.conify.perm_list.label "Permissions">
 <!ENTITY staff.main.menu.admin.server_admin.conify.copy_status.label "Copy Statuses">
-<!ENTITY staff.main.menu.admin.server_admin.conify.marc_code_maps.label "Coded Value Maps">
+<!ENTITY staff.main.menu.admin.server_admin.conify.marc_record_attrs.label "MARC Record Attributes">
+<!ENTITY staff.main.menu.admin.server_admin.conify.coded_value_maps.label "MARC Coded Value Maps">
 <!ENTITY staff.main.menu.admin.server_admin.conify.billing_type.label "Billing Types">
 <!ENTITY staff.main.menu.admin.server_admin.conify.z3950_source.label "Z39.50 Servers">
 <!ENTITY staff.main.menu.admin.server_admin.conify.circulation_modifier.label "Circulation Modifiers">
index 0e95c40..7ed0eda 100644 (file)
@@ -640,13 +640,6 @@ function holdsGetFormats() {
        var rec = holdArgs.record;
        var mrec = holdArgs.metarecord;
 
-       for( var i = 0; i < desc.length; i++ ) {
-               var d = desc[i];
-               if( type == 'T' && d.item_lang() != lang ) continue;
-               formats.push( _t_f_2_format(d.item_type(), d.item_form()));
-       }
-
-       formats = uniquify(formats);
 
        if( type == 'T') {
 
@@ -659,14 +652,27 @@ function holdsGetFormats() {
                                break;
                        }
                }
+
+       for( var i = 0; i < desc.length; i++ ) {
+               var d = desc[i];
+                   if( type == 'T' && d.item_lang() != lang ) continue;
+               formats.push( _t_f_2_format(d.item_type(), d.item_form()));
+           }
+
        } else if( type =='M') {
 
         // All available formats are selected by default in MR holds
-               for( var i = 0; i < formats.length; i++ ) {
-                       sformats.push(formats[i]);
-               }
+               for( var i = 0; i < desc.length; i++ ) {
+               var d = desc[i];
+                   var _tmp_f = _t_f_2_format(d.item_type(), d.item_form());
+               formats.push( _tmp_f );
+               sformats.push( _tmp_f );
+       }
        }
 
+       formats = uniquify(formats);
+       sformats = uniquify(sformats);
+
        return {
                lang : lang,
                avail_formats : formats, 
index 028e939..4b9346b 100644 (file)
@@ -141,6 +141,12 @@ oilsRptFolderManager.prototype.createTopFolder = function(type, orgsel) {
 
        folder.owner(USER.id());
        folder.parent(null);
+
+       /* Protect against empty folder names */
+       if (!DOM.oils_rpt_top_folder_name.value) {
+               return;
+       }
+
        folder.name(DOM.oils_rpt_top_folder_name.value);
        folder.shared(getSelectorVal(DOM.oils_rpt_top_folder_shared));
 
index 3b20241..627b713 100644 (file)
@@ -1,4 +1,5 @@
 [% WRAPPER default/base.tt2 %]
+[% ctx.page_title = 'MARC Record Attribute Definitions' %]
 <h1>Record Attribute Definitions</h1> <br/>
 
 <div dojoType="dijit.layout.ContentPane" layoutAlign="client" class='oils-header-panel'>
index fe74bdd..40d6265 100644 (file)
@@ -25,6 +25,7 @@ export NSIS_EXTRAOPTS
 export NSIS_WICON=$$(if [ -f client/evergreen.ico ]; then echo '-DWICON'; fi)
 export NSIS_AUTOUPDATE=$$([ -f client/defaults/preferences/autoupdate.js ] && echo '-DAUTOUPDATE')
 export NSIS_DEV=$$([ -f client/defaults/preferences/developers.js ] && echo '-DDEVELOPER')
+export NSIS_PERMACHINE=$$([ -f client/defaults/preferences/aa_per_machine.js ] && echo '-DPERMACHINE')
 
 #------------------------------
 # Build ILS XUL CLIENT/SERVER
@@ -40,6 +41,10 @@ devbuild: build
        @echo ' * Copying in developer preferences'
        @cp external/developers.js build/defaults/preferences/
 
+permachine: build
+       @echo ' * Copying in default to machine level registration file'
+       @cp external/aa_per_machine.js build/defaults/preferences/
+
 build: build_dir chrome2remote localize_manifest generated custom_skins open-ils stamp 
        @echo To test the staff client:
        @echo "  cd build/"
@@ -246,7 +251,7 @@ linux-xulrunner: client_app
 
 win-client: win-xulrunner
        @echo 'Building installer'
-       @makensis -DPRODUCT_VERSION="${STAFF_CLIENT_VERSION}" ${NSIS_WICON} ${NSIS_AUTOUPDATE} ${NSIS_DEV} ${NSIS_EXTRAOPTS} windowssetup.nsi
+       @makensis -DPRODUCT_VERSION="${STAFF_CLIENT_VERSION}" ${NSIS_WICON} ${NSIS_AUTOUPDATE} ${NSIS_DEV} ${NSIS_PERMACHINE} ${NSIS_EXTRAOPTS} windowssetup.nsi
        @echo 'Done'
 
 # For linux, just build a tar.bz2 archive
index f63ad8e..28c83af 100644 (file)
@@ -732,6 +732,10 @@ main.menu.prototype = {
                 ['oncommand'],
                 function() { open_eg_web_page('conify/global/config/record_attr_definition'); }
             ],
+            'cmd_server_admin_coded_value_map' : [
+                ['oncommand'],
+                function() { open_eg_web_page('conify/global/config/coded_value_map'); }
+            ],
             'cmd_server_admin_billing_type' : [
                 ['oncommand'],
                 function() { open_eg_web_page('conify/global/config/billing_type'); }
index a017100..0e71b6e 100644 (file)
     <command id="cmd_server_admin_perm_list"/>
     <command id="cmd_server_admin_copy_status"/>
     <command id="cmd_server_admin_marc_code"/>
+    <command id="cmd_server_admin_coded_value_map"/>
     <command id="cmd_server_admin_billing_type"/>
     <command id="cmd_server_admin_acq_invoice_item_type"/>
     <command id="cmd_server_admin_acq_invoice_payment_method"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.grp_tree.label;" command="cmd_server_admin_grp_tree"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.perm_list.label;" command="cmd_server_admin_perm_list"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.copy_status.label;" command="cmd_server_admin_copy_status"/>
-                <menuitem label="&staff.main.menu.admin.server_admin.conify.marc_code_maps.label;" command="cmd_server_admin_marc_code"/>
+                <menuitem label="&staff.main.menu.admin.server_admin.conify.marc_record_attrs.label;" command="cmd_server_admin_marc_code"/>
+                <menuitem label="&staff.main.menu.admin.server_admin.conify.coded_value_maps.label;" command="cmd_server_admin_coded_value_map"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.billing_type.label;" command="cmd_server_admin_billing_type"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.z3950_source.label;" command="cmd_server_admin_z39_source"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.circulation_modifier.label;" command="cmd_server_admin_circ_mod"/>
diff --git a/Open-ILS/xul/staff_client/external/aa_per_machine.js b/Open-ILS/xul/staff_client/external/aa_per_machine.js
new file mode 100644 (file)
index 0000000..863d023
--- /dev/null
@@ -0,0 +1,2 @@
+// Write to install directory instead of profile directory
+pref("open-ils.write_in_user_chrome_directory", false);
index 73f4c64..7ba79d2 100755 (executable)
@@ -119,7 +119,8 @@ function prep_update
 function check_file
 {
        CHECK_FILE="${1#$NEW/}"
-       if [ $CHECK_FILE == "update.manifest" ]; then
+       if [ $CHECK_FILE == "update.manifest" -o $CHECK_FILE == "defaults/preferences/developers.js" -o $CHECK_FILE == "defaults/preferences/aa_per_machine.js" ]; then
+        echo "Skipping $CHECK_FILE";
                return;
        fi
        DIR=$(dirname "$WORK/$CHECK_FILE")
@@ -165,7 +166,7 @@ function check_file
 function remove_file
 {
        RM_FILE="${1#$OLD/}"
-       if [ $RM_FILE != "update.manifest" ]; then
+       if [ $RM_FILE != "update.manifest" -a $RM_FILE != "defaults/preferences/developers.js" -a $RM_FILE != "defaults/preferences/aa_per_machine.js" -a $RM_FILE != "defaults/preferences/autoupdate.js" -a $RM_FILE != "defaults/preferences/autochannel.js" ]; then
                echo "remove \"$RM_FILE\"" >> "$MANIFEST"
        fi
 }
diff --git a/Open-ILS/xul/staff_client/extras.example.nsi b/Open-ILS/xul/staff_client/extras.example.nsi
new file mode 100644 (file)
index 0000000..7d0574c
--- /dev/null
@@ -0,0 +1,24 @@
+; Examples for extras file
+
+!ifdef EXTERNAL_EXTRAS_SECMAIN ; Main install block
+  ; Anything here will be done during install. Intended for shortcuts.
+
+  ; Useful examples include having an exe in the branding directory
+  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\My Program.lnk" "$INSTDIR\file.exe" "-somearg" "$INSTDIR\file.exe"
+
+  ; Or perhaps wanting a special link to start evergreen? You can even auto-detect icon usage:
+  !ifdef WICON
+    CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client Something.lnk" "$INSTDIR\evergreen.exe" "-something" "$INSTDIR\evergreen.ico"
+  !else
+    CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client Something.lnk" "$INSTDIR\evergreen.exe" "-something"
+  !endif
+!else ifdef EXTERNAL_EXTRAS_UNINSTALL ; Uninstall block
+  ; Anything you have added that you want removed may need uninstall lines
+
+  ; Such as that extra exe? Left a file and a link.
+  Delete "$INSTDIR\file.exe" 
+  Delete "$SMPROGRAMS\$ICONS_GROUP\My Program.lnk"
+
+  ; Or perhaps your extra start shortcuts?
+  Delete "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client Something.lnk"
+!endif
index 52b43d6..6db8455 100644 (file)
@@ -203,6 +203,7 @@ circ.renew.prototype = {
                         ['command'],
                         function() {
                             var p = { 
+                                'printer_context' : 'receipt',
                                 'template' : 'renew'
                             };
                             obj.list.print(p);
index 1bd51bf..40ab695 100644 (file)
 
             g.tb = document.getElementById('wsname');
 
+            var dnsService = Components.classes["@mozilla.org/network/dns-service;1"]
+                 .createInstance(Components.interfaces.nsIDNSService);
+
             render_menulist();
 
             document.getElementById('register_btn').disabled = false;
             document.getElementById('wsname').disabled = false;
-            document.getElementById('wsname').value = '';
+            if(dnsService.myHostName && dnsService.myHostName.toLowerCase() != 'localhost')
+                g.tb.value = dnsService.myHostName;
+            else
+                g.tb.value = '';
 
             document.getElementById('wsname').focus();
         }
index 1a7c038..6e1e08c 100644 (file)
@@ -1,15 +1,19 @@
 ; Script generated by the HM NIS Edit Script Wizard.
 
 ; HM NIS Edit Wizard helper defines
-!define PRODUCT_NAME "Evergreen Staff Client Trunk"
 ; Old versions of makensis don't like this, moved to Makefile
 ;!define /file PRODUCT_VERSION "client/VERSION"
+!define PRODUCT_TAG "Trunk"
+!define PRODUCT_NAME "Evergreen Staff Client ${PRODUCT_TAG}"
 !define PRODUCT_PUBLISHER "Evergreen Community"
 !define PRODUCT_WEB_SITE "http://evergreen-ils.org/"
 !define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\evergreen.exe"
 !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
 !define PRODUCT_UNINST_ROOT_KEY "HKLM"
 !define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir"
+!ifndef PRODUCT_LICENSE
+  !define PRODUCT_LICENSE "..\..\..\LICENSE.txt"
+!endif
 
 ; MUI 1.67 compatible ------
 !include "MUI.nsh"
 
 ; Welcome page
 !insertmacro MUI_PAGE_WELCOME
-; License page
-!insertmacro MUI_PAGE_LICENSE "..\..\..\LICENSE.txt"
+; License page, if we have one
+!if "${PRODUCT_LICENSE}" != ""
+  !insertmacro MUI_PAGE_LICENSE "${PRODUCT_LICENSE}"
+!endif
 ; Components page
-!ifdef AUTOUPDATE | DEVELOPER
+!ifdef AUTOUPDATE | DEVELOPER | PERMACHINE
 !insertmacro MUI_PAGE_COMPONENTS
 !endif
 ; Directory page
@@ -40,7 +46,7 @@
 ; Start menu page
 var ICONS_GROUP
 !define MUI_STARTMENUPAGE_NODISABLE
-!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Evergreen Staff Client Trunk"
+!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Evergreen Staff Client ${PRODUCT_TAG}"
 !define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
 !define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
 !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}"
@@ -63,32 +69,58 @@ var ICONS_GROUP
 
 Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
 OutFile "evergreen_staff_client_setup.exe"
-InstallDir "$PROGRAMFILES\Evergreen Staff Client Trunk"
+InstallDir "$PROGRAMFILES\Evergreen Staff Client ${PRODUCT_TAG}"
 InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
 ShowInstDetails show
 ShowUnInstDetails show
 RequestExecutionLevel admin
 
 Section "Staff Client" SECMAIN
+  ; Uninstall old (inno) variant?
+  IfFileExists "$INSTDIR/unin000.exe" 0 +3
+    ExecWait '"$INSTDIR/unins000.exe" /VERYSILENT'
+    Sleep 5000 ; Wait five seconds in case the uninstaller returned before it was done
+  ; Uninstall old (nsis) version?
+  IfFileExists "$INSTDIR/uninst.exe" 0 +4
+    ExecWait '"$INSTDIR/uninst.exe" /S _?="$INSTDIR"'
+    Sleep 5000 ; Wait five seconds in case the uninstaller returned before it was done
+    Delete "$INSTDIR/uninst.exe"
   SetOutPath "$INSTDIR"
-  File /r /x "autoupdate.js" /x "autochannel.js" /x "developers.js" "client\*"
+  File /r /x "autoupdate.js" /x "autochannel.js" /x "developers.js" /x "aa_per_machine.js" "client\*"
 
-; Shortcuts
+  ; Shortcuts
   !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
   CreateDirectory "$SMPROGRAMS\$ICONS_GROUP"
   !ifdef WICON
   CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client.lnk" "$INSTDIR\evergreen.exe" "" "$INSTDIR\evergreen.ico"
   !ifdef PROFILES
-  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client Profile Manager.lnk" "$INSTRDIR\evergreen.exe -profilemanager" "" "$INSTDIR\evergreen.ico"
+  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client Profile Manager.lnk" "$INSTDIR\evergreen.exe" "-profilemanager" "$INSTDIR\evergreen.ico"
   !endif
   !else
   CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client.lnk" "$INSTDIR\evergreen.exe"
   !ifdef PROFILES
-  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client Profile Manager.lnk" "$INSTRDIR\evergreen.exe -profilemanager"
+  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client Profile Manager.lnk" "$INSTDIR\evergreen.exe" "-profilemanager"
   !endif
   !endif
-  CreateShortCut "$DESKTOP\Evergreen Staff Client Trunk.lnk" "$INSTDIR\evergreen.exe"
+  CreateShortCut "$DESKTOP\Evergreen Staff Client ${PRODUCT_TAG}.lnk" "$INSTDIR\evergreen.exe"
+  
+  ; External script for extra things.
+  !define EXTERNAL_EXTRAS_SECMAIN
+  !include /NONFATAL "extras.nsi"
+  !undef EXTERNAL_EXTRAS_SECMAIN
+
   !insertmacro MUI_STARTMENU_WRITE_END
+
+  !ifdef AUTOUPDATE | PERMACHINE
+  ; For autoupdate and/or registering per machine, make sure we can write to the install directory.
+  ; If the AccessControl plugin was packaged or part of nsis we would use it instead.
+  ; Also, as cacls.exe is depreciated when icacls.exe exists, try icacls.exe first.
+  IfFileExists "$SYSDIR/icacls.exe" 0 +3
+  ExecWait '"$SYSDIR/icacls.exe" "$INSTDIR" /grant Everyone:(OI)(CI)F'  
+  Goto +3
+  IfFileExists "$SYSDIR/cacls.exe" 0 +2
+  ExecWait '"$SYSDIR/cacls.exe" "$INSTDIR" /E /G Everyone:F'
+  !endif
 SectionEnd
 
 !ifdef AUTOUPDATE
@@ -108,12 +140,19 @@ Section /o "Developer Options" SECDEV
 SectionEnd
 !endif
 
+!ifdef PERMACHINE
+Section /o "Registration Per Machine" SECPERMAC
+  SetOutPath "$INSTDIR\defaults\preferences"
+  File "client\defaults\preferences\aa_per_machine.js"
+  SetOutPath "$INSTDIR"
+SectionEnd
+!endif
 
 Function .onInit
   !insertmacro MUI_LANGDLL_DISPLAY
   SectionSetFlags ${SECMAIN} 17
   ; This is mainly for silent installs
-  !ifdef AUTOUPDATE | DEVELOPER
+  !ifdef AUTOUPDATE | DEVELOPER | PERMACHINE
     Var /GLOBAL CMD_ARGS
     StrCpy $CMD_ARGS ""
     ${GetParameters} $CMD_ARGS
@@ -127,6 +166,16 @@ Function .onInit
       !endif
       SectionSetFlags ${SECAUTO} 1
     !endif
+    !ifdef PERMACHINE
+      !ifdef PERMACHINE_NODEFAULT
+        ${GetOptions} $CMD_ARGS "/permachine" $0
+        IfErrors +2 0
+      !else
+        ${GetOptions} $CMD_ARGS "/nopermachine" $0
+        IfErrors 0 +2
+      !endif
+      SectionSetFlags ${SECPERMAC} 1
+    !endif
     !ifdef DEVELOPER
       ${GetOptions} $CMD_ARGS "/developer" $0
       IfErrors +2 0
@@ -163,20 +212,32 @@ SectionEnd
   !ifdef DEVELOPER
   !insertmacro MUI_DESCRIPTION_TEXT ${SECDEV}  "Developer Options"
   !endif
+  !ifdef PERMACHINE
+  !insertmacro MUI_DESCRIPTION_TEXT ${SECPERMAC}  "Default registration and offline storage to per machine instead of per user"
+  !endif
 !insertmacro MUI_FUNCTION_DESCRIPTION_END
 
 
 Function un.onUninstSuccess
   HideWindow
-  MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
+  MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." /SD IDOK
 FunctionEnd
 
 Function un.onInit
 !insertmacro MUI_UNGETLANGUAGE
-  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
+  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" /SD IDYES IDYES +2
   Abort
 FunctionEnd
 
+Function "un.RemoveFileCheck"
+  StrCmp $R7 "open_ils_staff_client" +5
+  StrCmp $R6 "" +3
+  Delete "$R9"
+  Goto +2
+  RmDir /r "$R9"
+  Push $0
+FunctionEnd
+
 Section Uninstall
   !insertmacro MUI_STARTMENU_GETFOLDER "Application" $ICONS_GROUP
   Delete "$INSTDIR\${PRODUCT_NAME}.url"
@@ -191,19 +252,32 @@ Section Uninstall
   Delete "$INSTDIR\chrome.manifest"
   Delete "$INSTDIR\updates.xml"
   Delete "$INSTDIR\log.txt"
+  Delete "$INSTDIR\evergreen.ico"
 
   Delete "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk"
   Delete "$SMPROGRAMS\$ICONS_GROUP\Website.lnk"
-  Delete "$DESKTOP\Evergreen Staff Client Trunk.lnk"
+  Delete "$DESKTOP\Evergreen Staff Client ${PRODUCT_TAG}.lnk"
   Delete "$SMPROGRAMS\$ICONS_GROUP\Evergreen Staff Client.lnk"
 
+  ; External script for removing extra files before we wipe out the install directory
+  !define EXTERNAL_EXTRAS_UNINSTALL
+  !include /NONFATAL "extras.nsi"
+  !undef EXTERNAL_EXTRAS_UNINSTALL
+
   RMDir "$SMPROGRAMS\$ICONS_GROUP"
   RMDir /r "$INSTDIR\updates"
   RMDir /r "$INSTDIR\xulrunner"
   RMDir /r "$INSTDIR\extensions"
-  RMDir /r "$INSTDIR\chrome"
+;  RMDir /r "$INSTDIR\chrome" ; We can't wipe out the chrome directory normally. Per-machine info would be lost on upgrade.
   RMDir /r "$INSTDIR\components"
   RMDir /r "$INSTDIR\defaults"
+
+  ; Basically, we want to remove everything but "open_ils_staff_client" from the chrome dir
+  ; So we pass over every file and folder in it. If it matches, we leave it alone.
+  ${Locate} "$INSTDIR\chrome" "/G=0" "un.RemoveFileCheck"
+  ; Then, we remove the folder non-recursively, which won't wipe out the folder if it is still there.
+  RMDir "$INSTDIR\chrome"
+
   RMDir "$INSTDIR"
 
   DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"