First rough 2.4 - 2.7 database upgrade for Conifer
authorDan Scott <dscott@laurentian.ca>
Mon, 24 Nov 2014 20:21:20 +0000 (15:21 -0500)
committerDan Scott <dscott@laurentian.ca>
Mon, 24 Nov 2014 20:21:20 +0000 (15:21 -0500)
Nothing special, you know.

Signed-off-by: Dan Scott <dscott@laurentian.ca>
Open-ILS/src/sql/Pg/upgrade/conifer_2_4-to_2_7.sql [new file with mode: 0644]

diff --git a/Open-ILS/src/sql/Pg/upgrade/conifer_2_4-to_2_7.sql b/Open-ILS/src/sql/Pg/upgrade/conifer_2_4-to_2_7.sql
new file mode 100644 (file)
index 0000000..7bfbd30
--- /dev/null
@@ -0,0 +1,25916 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0794', :eg_version);
+
+INSERT INTO config.standing_penalty (id,name,label,block_list,staff_alert)
+    VALUES (5,'PATRON_EXCEEDS_LOST_COUNT',oils_i18n_gettext(5, 'Patron exceeds max lost item threshold', 'csp', 'label'),'CIRC|FULFILL|HOLD|CAPTURE|RENEW', TRUE);
+
+INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype ) VALUES (
+    'circ.tally_lost', 'circ',
+    oils_i18n_gettext(
+        'circ.tally_lost',
+        'Include Lost circulations in lump sum tallies in Patron Display.',
+        'coust',
+        'label'),
+    oils_i18n_gettext(
+        'circ.tally_lost',
+        'In the Patron Display interface, the number of total active circulations for a given patron is presented in the Summary sidebar and underneath the Items Out navigation button.  This setting will include Lost circulations as counting toward these tallies.',
+        'coust',
+        'description'),
+    'bool'
+);
+
+-- Function: actor.calculate_system_penalties(integer, integer)
+-- DROP FUNCTION actor.calculate_system_penalties(integer, integer);
+
+CREATE OR REPLACE FUNCTION actor.calculate_system_penalties(match_user integer, context_org integer)
+    RETURNS SETOF actor.usr_standing_penalty AS
+$BODY$
+DECLARE
+    user_object             actor.usr%ROWTYPE;
+    new_sp_row            actor.usr_standing_penalty%ROWTYPE;
+    existing_sp_row       actor.usr_standing_penalty%ROWTYPE;
+    collections_fines      permission.grp_penalty_threshold%ROWTYPE;
+    max_fines               permission.grp_penalty_threshold%ROWTYPE;
+    max_overdue           permission.grp_penalty_threshold%ROWTYPE;
+    max_items_out       permission.grp_penalty_threshold%ROWTYPE;
+    max_lost                        permission.grp_penalty_threshold%ROWTYPE;
+    tmp_grp                 INT;
+    items_overdue        INT;
+    items_out              INT;
+    items_lost             INT;
+    context_org_list     INT[];
+    current_fines          NUMERIC(8,2) := 0.0;
+    tmp_fines               NUMERIC(8,2);
+    tmp_groc               RECORD;
+    tmp_circ                RECORD;
+    tmp_org                actor.org_unit%ROWTYPE;
+    tmp_penalty          config.standing_penalty%ROWTYPE;
+    tmp_depth            INTEGER;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+
+    -- Max fines
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has a high fine balance
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 1 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_fines.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 1;
+
+        SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                      FROM  booking.reservation r
+                      WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                      FROM  money.grocery g
+                      WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                      FROM  action.circulation circ
+                      WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL ) l USING (id);
+
+        IF current_fines >= max_fines.threshold THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_fines.org_unit;
+            new_sp_row.standing_penalty := 1;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max overdue
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many overdue items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+
+            SELECT * INTO max_overdue FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 2 AND org_unit = tmp_org.id;
+
+            IF max_overdue.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_overdue.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_overdue.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_overdue.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 2;
+
+        SELECT  INTO items_overdue COUNT(*)
+          FROM  action.circulation circ
+                JOIN  actor.org_unit_full_path( max_overdue.org_unit ) fp ON (circ.circ_lib = fp.id)
+          WHERE circ.usr = match_user
+            AND circ.checkin_time IS NULL
+            AND circ.due_date < NOW()
+            AND (circ.stop_fines = 'MAXFINES' OR circ.stop_fines IS NULL);
+
+        IF items_overdue >= max_overdue.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_overdue.org_unit;
+            new_sp_row.standing_penalty := 2;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max out
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many checked out items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_items_out FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 3 AND org_unit = tmp_org.id;
+
+            IF max_items_out.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_items_out.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    -- Fail if the user has too many items checked out
+    IF max_items_out.threshold IS NOT NULL THEN
+        RETURN QUERY
+            SELECT  *
+            FROM  actor.usr_standing_penalty
+            WHERE usr = match_user
+                AND org_unit = max_items_out.org_unit
+                AND (stop_date IS NULL or stop_date > NOW())
+                AND standing_penalty = 3;
+            SELECT  INTO items_out COUNT(*)
+            FROM  action.circulation circ
+            JOIN  actor.org_unit_full_path( max_items_out.org_unit ) fp ON (circ.circ_lib = fp.id)
+            WHERE circ.usr = match_user
+                AND circ.checkin_time IS NULL
+                AND (circ.stop_fines IN (
+                    SELECT 'MAXFINES'::TEXT
+                    UNION ALL
+                    SELECT 'LONGOVERDUE'::TEXT
+                    UNION ALL
+                    SELECT 'LOST'::TEXT
+                    WHERE 'true' ILIKE
+                    (
+                        SELECT CASE
+                            WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.tally_lost', circ.circ_lib)) ILIKE 'true' THEN 'true'
+                            ELSE 'false'
+                        END
+                    )
+                    UNION ALL
+                    SELECT 'CLAIMSRETURNED'::TEXT
+                    WHERE 'false' ILIKE
+                        (
+                            SELECT CASE
+                            WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.do_not_tally_claims_returned', circ.circ_lib)) ILIKE 'true' THEN 'true'
+                            ELSE 'false'
+                            END
+                        )
+                    ) OR circ.stop_fines IS NULL)
+                AND xact_finish IS NULL;
+
+    IF items_out >= max_items_out.threshold::INT THEN
+        new_sp_row.usr := match_user;
+        new_sp_row.org_unit := max_items_out.org_unit;
+        new_sp_row.standing_penalty := 3;
+        RETURN NEXT new_sp_row;
+   END IF;
+END IF;
+
+    -- Start over for max lost
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many lost items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_lost FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 5 AND org_unit = tmp_org.id;
+            IF max_lost.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_lost.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_lost.threshold IS NOT NULL THEN
+        RETURN QUERY
+            SELECT  *
+            FROM  actor.usr_standing_penalty
+            WHERE usr = match_user
+                AND org_unit = max_lost.org_unit
+                AND (stop_date IS NULL or stop_date > NOW())
+                AND standing_penalty = 5;
+
+        SELECT INTO items_lost COUNT(*)
+        FROM  action.circulation circ
+            JOIN  actor.org_unit_full_path( max_lost.org_unit ) fp ON (circ.circ_lib = fp.id)
+            WHERE circ.usr = match_user
+                AND circ.checkin_time IS NULL
+                AND (circ.stop_fines = 'LOST')
+                AND xact_finish IS NULL;
+
+        IF items_lost >= max_lost.threshold::INT AND 0 < max_lost.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_lost.org_unit;
+            new_sp_row.standing_penalty := 5;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for collections warning
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has a collections-level fine balance
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 4 AND org_unit = tmp_org.id;
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+                FROM  actor.usr_standing_penalty
+                WHERE usr = match_user
+                    AND org_unit = max_fines.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 4;
+
+        SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        SELECT  SUM(f.balance_owed) INTO current_fines
+            FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                        FROM  booking.reservation r
+                        WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND r.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                        FROM  money.grocery g
+                        WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND g.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                        FROM  action.circulation circ
+                        WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND circ.xact_finish IS NULL ) l USING (id);
+
+        IF current_fines >= max_fines.threshold THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_fines.org_unit;
+            new_sp_row.standing_penalty := 4;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for in collections
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Remove the in-collections penalty if the user has paid down enough
+    -- This penalty is different, because this code is not responsible for creating 
+    -- new in-collections penalties, only for removing them
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 30 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        -- first, see if the user had paid down to the threshold
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                        FROM  booking.reservation r
+                        WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND r.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                        FROM  money.grocery g
+                        WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND g.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                        FROM  action.circulation circ
+                        WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND circ.xact_finish IS NULL ) l USING (id);
+
+        IF current_fines IS NULL OR current_fines <= max_fines.threshold THEN
+            -- patron has paid down enough
+
+            SELECT INTO tmp_penalty * FROM config.standing_penalty WHERE id = 30;
+
+            IF tmp_penalty.org_depth IS NOT NULL THEN
+
+                -- since this code is not responsible for applying the penalty, it can't 
+                -- guarantee the current context org will match the org at which the penalty 
+                --- was applied.  search up the org tree until we hit the configured penalty depth
+                SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+                SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+
+                WHILE tmp_depth >= tmp_penalty.org_depth LOOP
+
+                    RETURN QUERY
+                        SELECT  *
+                            FROM  actor.usr_standing_penalty
+                            WHERE usr = match_user
+                                AND org_unit = tmp_org.id
+                                AND (stop_date IS NULL or stop_date > NOW())
+                                AND standing_penalty = 30;
+
+                    IF tmp_org.parent_ou IS NULL THEN
+                        EXIT;
+                    END IF;
+
+                    SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+                    SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+                END LOOP;
+
+            ELSE
+
+                -- no penalty depth is defined, look for exact matches
+
+                RETURN QUERY
+                    SELECT  *
+                        FROM  actor.usr_standing_penalty
+                        WHERE usr = match_user
+                            AND org_unit = max_fines.org_unit
+                            AND (stop_date IS NULL or stop_date > NOW())
+                            AND standing_penalty = 30;
+            END IF;
+
+        END IF;
+
+    END IF;
+
+    RETURN;
+END;
+$BODY$
+    LANGUAGE plpgsql VOLATILE
+    COST 100
+    ROWS 1000;
+
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0795', :eg_version); -- berick/dbwells
+
+CREATE OR REPLACE FUNCTION 
+    evergreen.z3950_attr_name_is_valid(TEXT) RETURNS BOOLEAN AS $func$
+    SELECT EXISTS (SELECT 1 FROM config.z3950_attr WHERE name = $1);
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+COMMENT ON FUNCTION evergreen.z3950_attr_name_is_valid(TEXT) IS $$
+Results in TRUE if there exists at least one config.z3950_attr
+with the provided name.  Used by config.z3950_index_field_map
+to verify z3950_attr_type maps.
+$$;
+
+CREATE TABLE config.z3950_index_field_map (
+    id              SERIAL  PRIMARY KEY,
+    label           TEXT    NOT NULL, -- i18n
+    metabib_field   INTEGER REFERENCES config.metabib_field(id),
+    record_attr     TEXT    REFERENCES config.record_attr_definition(name),
+    z3950_attr      INTEGER REFERENCES config.z3950_attr(id),
+    z3950_attr_type TEXT,-- REFERENCES config.z3950_attr(name)
+    CONSTRAINT metabib_field_or_record_attr CHECK (
+        metabib_field IS NOT NULL OR 
+        record_attr IS NOT NULL
+    ),
+    CONSTRAINT attr_or_attr_type CHECK (
+        z3950_attr IS NOT NULL OR 
+        z3950_attr_type IS NOT NULL
+    ),
+    -- ensure the selected z3950_attr_type refers to a valid attr name
+    CONSTRAINT valid_z3950_attr_type CHECK (
+        z3950_attr_type IS NULL OR 
+            evergreen.z3950_attr_name_is_valid(z3950_attr_type)
+    )
+);
+
+-- seed data
+
+INSERT INTO config.z3950_index_field_map 
+    (id, label, metabib_field, z3950_attr_type) VALUES 
+(1, oils_i18n_gettext(1, 'Title',   'czifm', 'label'), 5,  'title'),
+(2, oils_i18n_gettext(2, 'Author',  'czifm', 'label'), 8,  'author'),
+(3, oils_i18n_gettext(3, 'ISBN',    'czifm', 'label'), 18, 'isbn'),
+(4, oils_i18n_gettext(4, 'ISSN',    'czifm', 'label'), 19, 'issn'),
+(5, oils_i18n_gettext(5, 'LCCN',    'czifm', 'label'), 30, 'lccn');
+
+INSERT INTO config.z3950_index_field_map 
+    (id, label, record_attr, z3950_attr_type) VALUES 
+(6, oils_i18n_gettext(6, 'Pubdate',  'czifm', 'label'),'pubdate', 'pubdate'),
+(7, oils_i18n_gettext(7, 'Item Type', 'czifm', 'label'),'item_type', 'item_type');
+
+
+-- let's leave room for more stock mappings
+SELECT SETVAL('config.z3950_index_field_map_id_seq'::TEXT, 1000);
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'cat.z3950.batch.max_parallel',
+        'cat',
+        oils_i18n_gettext(
+            'cat.z3950.batch.max_parallel',
+            'Maximum Parallel Z39.50 Batch Searches',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'cat.z3950.batch.max_parallel',
+            'The maximum number of Z39.50 searches that can be in-flight at any given time when performing batch Z39.50 searches',
+            'coust',
+            'description'
+        ),
+        'integer'
+    );
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'cat.z3950.batch.max_results',
+        'cat',
+        oils_i18n_gettext(
+            'cat.z3950.batch.max_results',
+            'Maximum Z39.50 Batch Search Results',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'cat.z3950.batch.max_results',
+            'The maximum number of search results to retrieve and queue for each record + Z39 source during batch Z39.50 searches',
+            'coust',
+            'description'
+        ),
+        'integer'
+    );
+
+INSERT INTO vandelay.bib_attr_definition (id, code, description, xpath) 
+    VALUES (
+        16, 
+        'zsource',
+        oils_i18n_gettext(16, 'Z39.50 Source', 'vqbrad', 'description'),
+        '//*[@tag="901"]/*[@code="z"]'
+    );
+
+
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0796', :eg_version); -- berick/dbwells
+
+ALTER TABLE vandelay.bib_queue ADD COLUMN match_bucket
+   INTEGER REFERENCES container.biblio_record_entry_bucket(id)
+   ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;  
+
+CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
+DECLARE
+    incoming_existing_id    TEXT;
+    test_result             vandelay.match_set_test_result%ROWTYPE;
+    tmp_rec                 BIGINT;
+    match_set               INT;
+    match_bucket            INT;
+BEGIN
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
+
+    SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
+
+    IF match_set IS NOT NULL THEN
+        NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
+    END IF;
+
+    -- Perfect matches on 901$c exit early with a match with high quality.
+    incoming_existing_id :=
+        oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
+
+    IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
+        SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
+        IF tmp_rec IS NOT NULL THEN
+            INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality) 
+                SELECT
+                    NEW.id, 
+                    b.id,
+                    9999,
+                    -- note: no match_set means quality==0
+                    vandelay.measure_record_quality( b.marc, match_set )
+                FROM biblio.record_entry b
+                WHERE id = incoming_existing_id::bigint;
+        END IF;
+    END IF;
+
+    IF match_set IS NULL THEN
+        RETURN NEW;
+    END IF;
+
+    SELECT q.match_bucket INTO match_bucket FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
+
+    FOR test_result IN SELECT * FROM
+        vandelay.match_set_test_marcxml(match_set, NEW.marc, match_bucket) LOOP
+
+        INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
+            SELECT  
+                NEW.id,
+                test_result.record,
+                test_result.quality,
+                vandelay.measure_record_quality( b.marc, match_set )
+               FROM  biblio.record_entry b
+               WHERE id = test_result.record;
+
+    END LOOP;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+DROP FUNCTION IF EXISTS vandelay.match_set_test_marcxml(INTEGER, TEXT);
+
+CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
+    match_set_id INTEGER, record_xml TEXT, bucket_id INTEGER 
+) RETURNS SETOF vandelay.match_set_test_result AS $$
+DECLARE
+    tags_rstore HSTORE;
+    svf_rstore  HSTORE;
+    coal        TEXT;
+    joins       TEXT;
+    query_      TEXT;
+    wq          TEXT;
+    qvalue      INTEGER;
+    rec         RECORD;
+BEGIN
+    tags_rstore := vandelay.flatten_marc_hstore(record_xml);
+    svf_rstore := vandelay.extract_rec_attrs(record_xml);
+
+    CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
+    CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
+
+    -- generate the where clause and return that directly (into wq), and as
+    -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
+    wq := vandelay.get_expr_from_match_set(match_set_id, tags_rstore);
+
+    query_ := 'SELECT DISTINCT(record), ';
+
+    -- qrows table is for the quality bits we add to the SELECT clause
+    SELECT ARRAY_TO_STRING(
+        ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
+    ) INTO coal FROM _vandelay_tmp_qrows;
+
+    -- our query string so far is the SELECT clause and the inital FROM.
+    -- no JOINs yet nor the WHERE clause
+    query_ := query_ || coal || ' AS quality ' || E'\n';
+
+    -- jrows table is for the joins we must make (and the real text conditions)
+    SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
+        FROM _vandelay_tmp_jrows;
+
+    -- add those joins and the where clause to our query.
+    query_ := query_ || joins || E'\n';
+
+    -- join the record bucket
+    IF bucket_id IS NOT NULL THEN
+        query_ := query_ || 'JOIN container.biblio_record_entry_bucket_item ' ||
+            'brebi ON (brebi.target_biblio_record_entry = record ' ||
+            'AND brebi.bucket = ' || bucket_id || E')\n';
+    END IF;
+
+    query_ := query_ || 'JOIN biblio.record_entry bre ON (bre.id = record) ' || 'WHERE ' || wq || ' AND not bre.deleted';
+
+    -- this will return rows of record,quality
+    FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
+        RETURN NEXT rec;
+    END LOOP;
+
+    DROP TABLE _vandelay_tmp_qrows;
+    DROP TABLE _vandelay_tmp_jrows;
+    RETURN;
+END;
+$$ LANGUAGE PLPGSQL;
+
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0797', :eg_version); -- tsbere/Dyrcona/dbwells
+
+-- New global flags for the purge function
+INSERT INTO config.global_flag  (name, label, enabled)
+    VALUES (
+        'history.hold.retention_age',
+        oils_i18n_gettext('history.hold.retention_age', 'Historical Hold Retention Age', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_fulfilled',
+        oils_i18n_gettext('history.hold.retention_age_fulfilled', 'Historical Hold Retention Age - Fulfilled', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled',
+        oils_i18n_gettext('history.hold.retention_age_canceled', 'Historical Hold Retention Age - Canceled (Default)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_1',
+        oils_i18n_gettext('history.hold.retention_age_canceled_1', 'Historical Hold Retention Age - Canceled (Untarged expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_2',
+        oils_i18n_gettext('history.hold.retention_age_canceled_2', 'Historical Hold Retention Age - Canceled (Hold Shelf expiration)', 'cgf', 'label'),
+        FALSE
+    ),(
+        'history.hold.retention_age_canceled_3',
+        oils_i18n_gettext('history.hold.retention_age_canceled_3', 'Historical Hold Retention Age - Canceled (Patron via phone)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_4',
+        oils_i18n_gettext('history.hold.retention_age_canceled_4', 'Historical Hold Retention Age - Canceled (Patron in person)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_5',
+        oils_i18n_gettext('history.hold.retention_age_canceled_5', 'Historical Hold Retention Age - Canceled (Staff forced)', 'cgf', 'label'),
+        TRUE
+    ),(
+        'history.hold.retention_age_canceled_6',
+        oils_i18n_gettext('history.hold.retention_age_canceled_6', 'Historical Hold Retention Age - Canceled (Patron via OPAC)', 'cgf', 'label'),
+        FALSE
+    );
+
+CREATE OR REPLACE FUNCTION action.purge_holds() RETURNS INT AS $func$
+DECLARE
+  current_hold RECORD;
+  purged_holds INT;
+  cgf_d INTERVAL;
+  cgf_f INTERVAL;
+  cgf_c INTERVAL;
+  prev_usr INT;
+  user_start TIMESTAMPTZ;
+  user_age INTERVAL;
+  user_count INT;
+BEGIN
+  purged_holds := 0;
+  SELECT INTO cgf_d value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age' AND enabled;
+  SELECT INTO cgf_f value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_fulfilled' AND enabled;
+  SELECT INTO cgf_c value::INTERVAL FROM config.global_flag WHERE name = 'history.hold.retention_age_canceled' AND enabled;
+  FOR current_hold IN
+    SELECT
+      rank() OVER (PARTITION BY usr ORDER BY COALESCE(fulfillment_time, cancel_time) DESC),
+      cgf_cs.value::INTERVAL as cgf_cs,
+      ahr.*
+    FROM
+      action.hold_request ahr
+      LEFT JOIN config.global_flag cgf_cs ON (ahr.cancel_cause IS NOT NULL AND cgf_cs.name = 'history.hold.retention_age_canceled_' || ahr.cancel_cause AND cgf_cs.enabled)
+    WHERE
+      (fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL)
+  LOOP
+    IF prev_usr IS NULL OR prev_usr != current_hold.usr THEN
+      prev_usr := current_hold.usr;
+      SELECT INTO user_start oils_json_to_text(value)::TIMESTAMPTZ FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_start';
+      SELECT INTO user_age oils_json_to_text(value)::INTERVAL FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_age';
+      SELECT INTO user_count oils_json_to_text(value)::INT FROM actor.usr_setting WHERE usr = prev_usr AND name = 'history.hold.retention_count';
+      IF user_start IS NOT NULL THEN
+        user_age := LEAST(user_age, AGE(NOW(), user_start));
+      END IF;
+      IF user_count IS NULL THEN
+        user_count := 1000; -- Assumption based on the user visible holds routine
+      END IF;
+    END IF;
+    -- Library keep age trumps user keep anything, for purposes of being able to hold on to things when staff canceled and such.
+    IF current_hold.fulfillment_time IS NOT NULL AND current_hold.fulfillment_time > NOW() - COALESCE(cgf_f, cgf_d) THEN
+      CONTINUE;
+    END IF;
+    IF current_hold.cancel_time IS NOT NULL AND current_hold.cancel_time > NOW() - COALESCE(current_hold.cgf_cs, cgf_c, cgf_d) THEN
+      CONTINUE;
+    END IF;
+
+    -- User keep age needs combining with count. If too old AND within the count, keep!
+    IF user_start IS NOT NULL AND COALESCE(current_hold.fulfillment_time, current_hold.cancel_time) > NOW() - user_age AND current_hold.rank <= user_count THEN
+      CONTINUE;
+    END IF;
+
+    -- All checks should have passed, delete!
+    DELETE FROM action.hold_request WHERE id = current_hold.id;
+    purged_holds := purged_holds + 1;
+  END LOOP;
+  RETURN purged_holds;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION action.usr_visible_holds (usr_id INT) RETURNS SETOF action.hold_request AS $func$
+DECLARE
+    h               action.hold_request%ROWTYPE;
+    view_age        INTERVAL;
+    view_count      INT;
+    usr_view_count  actor.usr_setting%ROWTYPE;
+    usr_view_age    actor.usr_setting%ROWTYPE;
+    usr_view_start  actor.usr_setting%ROWTYPE;
+BEGIN
+    SELECT * INTO usr_view_count FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_count';
+    SELECT * INTO usr_view_age FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_age';
+    SELECT * INTO usr_view_start FROM actor.usr_setting WHERE usr = usr_id AND name = 'history.hold.retention_start';
+
+    FOR h IN
+        SELECT  *
+          FROM  action.hold_request
+          WHERE usr = usr_id
+                AND fulfillment_time IS NULL
+                AND cancel_time IS NULL
+          ORDER BY request_time DESC
+    LOOP
+        RETURN NEXT h;
+    END LOOP;
+
+    IF usr_view_start.value IS NULL THEN
+        RETURN;
+    END IF;
+
+    IF usr_view_age.value IS NOT NULL THEN
+        -- User opted in and supplied a retention age
+        IF oils_json_to_text(usr_view_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ) THEN
+            view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
+        ELSE
+            view_age := oils_json_to_text(usr_view_age.value)::INTERVAL;
+        END IF;
+    ELSE
+        -- User opted in
+        view_age := AGE(NOW(), oils_json_to_text(usr_view_start.value)::TIMESTAMPTZ);
+    END IF;
+
+    IF usr_view_count.value IS NOT NULL THEN
+        view_count := oils_json_to_text(usr_view_count.value)::INT;
+    ELSE
+        view_count := 1000;
+    END IF;
+
+    -- show some fulfilled/canceled holds
+    FOR h IN
+        SELECT  *
+          FROM  action.hold_request
+          WHERE usr = usr_id
+                AND ( fulfillment_time IS NOT NULL OR cancel_time IS NOT NULL )
+                AND COALESCE(fulfillment_time, cancel_time) > NOW() - view_age
+          ORDER BY COALESCE(fulfillment_time, cancel_time) DESC
+          LIMIT view_count
+    LOOP
+        RETURN NEXT h;
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TABLE action.aged_hold_request (
+    usr_post_code              TEXT,
+    usr_home_ou                INT     NOT NULL,
+    usr_profile                INT     NOT NULL,
+    usr_birth_year             INT,
+    staff_placed        BOOLEAN NOT NULL,
+    LIKE action.hold_request
+);
+ALTER TABLE action.aged_hold_request
+      ADD PRIMARY KEY (id),
+      DROP COLUMN usr,
+      DROP COLUMN requestor,
+      DROP COLUMN sms_carrier,
+      ALTER COLUMN phone_notify TYPE BOOLEAN
+            USING CASE WHEN phone_notify IS NULL OR phone_notify = '' THEN FALSE ELSE TRUE END,
+      ALTER COLUMN sms_notify TYPE BOOLEAN
+            USING CASE WHEN sms_notify IS NULL OR sms_notify = '' THEN FALSE ELSE TRUE END,
+      ALTER COLUMN phone_notify SET NOT NULL,
+      ALTER COLUMN sms_notify SET NOT NULL;
+CREATE INDEX aged_hold_request_target_idx ON action.aged_hold_request (target);
+CREATE INDEX aged_hold_request_pickup_lib_idx ON action.aged_hold_request (pickup_lib);
+CREATE INDEX aged_hold_request_current_copy_idx ON action.aged_hold_request (current_copy);
+CREATE INDEX aged_hold_request_fulfillment_staff_idx ON action.aged_hold_request ( fulfillment_staff );
+
+CREATE OR REPLACE VIEW action.all_hold_request AS
+    SELECT DISTINCT
+           COALESCE(a.post_code, b.post_code) AS usr_post_code,
+           p.home_ou AS usr_home_ou,
+           p.profile AS usr_profile,
+           EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
+           CAST(ahr.requestor <> ahr.usr AS BOOLEAN) AS staff_placed,
+           ahr.id,
+           ahr.request_time,
+           ahr.capture_time,
+           ahr.fulfillment_time,
+           ahr.checkin_time,
+           ahr.return_time,
+           ahr.prev_check_time,
+           ahr.expire_time,
+           ahr.cancel_time,
+           ahr.cancel_cause,
+           ahr.cancel_note,
+           ahr.target,
+           ahr.current_copy,
+           ahr.fulfillment_staff,
+           ahr.fulfillment_lib,
+           ahr.request_lib,
+           ahr.selection_ou,
+           ahr.selection_depth,
+           ahr.pickup_lib,
+           ahr.hold_type,
+           ahr.holdable_formats,
+           CASE
+           WHEN ahr.phone_notify IS NULL THEN FALSE
+           WHEN ahr.phone_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS phone_notify,
+           ahr.email_notify,
+           CASE
+           WHEN ahr.sms_notify IS NULL THEN FALSE
+           WHEN ahr.sms_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS sms_notify,
+           ahr.frozen,
+           ahr.thaw_date,
+           ahr.shelf_time,
+           ahr.cut_in_line,
+           ahr.mint_condition,
+           ahr.shelf_expire_time,
+           ahr.current_shelf_lib
+    FROM action.hold_request ahr
+         JOIN actor.usr p ON (ahr.usr = p.id)
+         LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
+         LEFT JOIN actor.usr_address b ON (p.billing_address = b.id)
+    UNION ALL
+    SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib
+    FROM action.aged_hold_request;
+
+CREATE OR REPLACE FUNCTION action.age_hold_on_delete () RETURNS TRIGGER AS $$
+DECLARE
+BEGIN
+    -- Archive a copy of the old row to action.aged_hold_request
+
+    INSERT INTO action.aged_hold_request
+           (usr_post_code,
+            usr_home_ou,
+            usr_profile,
+            usr_birth_year,
+            staff_placed,
+            id,
+            request_time,
+            capture_time,
+            fulfillment_time,
+            checkin_time,
+            return_time,
+            prev_check_time,
+            expire_time,
+            cancel_time,
+            cancel_cause,
+            cancel_note,
+            target,
+            current_copy,
+            fulfillment_staff,
+            fulfillment_lib,
+            request_lib,
+            selection_ou,
+            selection_depth,
+            pickup_lib,
+            hold_type,
+            holdable_formats,
+            phone_notify,
+            email_notify,
+            sms_notify,
+            frozen,
+            thaw_date,
+            shelf_time,
+            cut_in_line,
+            mint_condition,
+            shelf_expire_time,
+            current_shelf_lib)
+      SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib
+        FROM action.all_hold_request WHERE id = OLD.id;
+
+    RETURN OLD;
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE TRIGGER action_hold_request_aging_tgr
+       BEFORE DELETE ON action.hold_request
+       FOR EACH ROW
+       EXECUTE PROCEDURE action.age_hold_on_delete ();
+
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0798', :eg_version); -- tsbere/Dyrcona/dbwells
+
+INSERT INTO config.global_flag (name, label)
+    VALUES (
+        'history.circ.retention_uses_last_finished',
+        oils_i18n_gettext(
+            'history.circ.retention_uses_last_finished',
+            'Historical Circulations use most recent xact_finish date instead of last circ''s.',
+            'cgf',
+            'label'
+        )
+    ),(
+        'history.circ.retention_age_is_min',
+        oils_i18n_gettext(
+            'history.circ.retention_age_is_min',
+            'Historical Circulations are kept for global retention age at a minimum, regardless of user preferences.',
+            'cgf',
+            'label'
+        )
+    );
+
+
+-- Drop old variants
+DROP FUNCTION IF EXISTS action.circ_chain(INTEGER);
+DROP FUNCTION IF EXISTS action.summarize_circ_chain(INTEGER);
+
+CREATE OR REPLACE FUNCTION action.circ_chain ( ctx_circ_id BIGINT ) RETURNS SETOF action.circulation AS $$
+DECLARE
+    tmp_circ action.circulation%ROWTYPE;
+    circ_0 action.circulation%ROWTYPE;
+BEGIN
+
+    SELECT INTO tmp_circ * FROM action.circulation WHERE id = ctx_circ_id;
+
+    IF tmp_circ IS NULL THEN
+        RETURN NEXT tmp_circ;
+    END IF;
+    circ_0 := tmp_circ;
+
+    -- find the front of the chain
+    WHILE TRUE LOOP
+        SELECT INTO tmp_circ * FROM action.circulation WHERE id = tmp_circ.parent_circ;
+        IF tmp_circ IS NULL THEN
+            EXIT;
+        END IF;
+        circ_0 := tmp_circ;
+    END LOOP;
+
+    -- now send the circs to the caller, oldest to newest
+    tmp_circ := circ_0;
+    WHILE TRUE LOOP
+        IF tmp_circ IS NULL THEN
+            EXIT;
+        END IF;
+        RETURN NEXT tmp_circ;
+        SELECT INTO tmp_circ * FROM action.circulation WHERE parent_circ = tmp_circ.id;
+    END LOOP;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION action.summarize_circ_chain ( ctx_circ_id BIGINT ) RETURNS action.circ_chain_summary AS $$
+
+DECLARE
+
+    -- first circ in the chain
+    circ_0 action.circulation%ROWTYPE;
+
+    -- last circ in the chain
+    circ_n action.circulation%ROWTYPE;
+
+    -- circ chain under construction
+    chain action.circ_chain_summary;
+    tmp_circ action.circulation%ROWTYPE;
+
+BEGIN
+    
+    chain.num_circs := 0;
+    FOR tmp_circ IN SELECT * FROM action.circ_chain(ctx_circ_id) LOOP
+
+        IF chain.num_circs = 0 THEN
+            circ_0 := tmp_circ;
+        END IF;
+
+        chain.num_circs := chain.num_circs + 1;
+        circ_n := tmp_circ;
+    END LOOP;
+
+    chain.start_time := circ_0.xact_start;
+    chain.last_stop_fines := circ_n.stop_fines;
+    chain.last_stop_fines_time := circ_n.stop_fines_time;
+    chain.last_checkin_time := circ_n.checkin_time;
+    chain.last_checkin_scan_time := circ_n.checkin_scan_time;
+    SELECT INTO chain.checkout_workstation name FROM actor.workstation WHERE id = circ_0.workstation;
+    SELECT INTO chain.last_checkin_workstation name FROM actor.workstation WHERE id = circ_n.checkin_workstation;
+
+    IF chain.num_circs > 1 THEN
+        chain.last_renewal_time := circ_n.xact_start;
+        SELECT INTO chain.last_renewal_workstation name FROM actor.workstation WHERE id = circ_n.workstation;
+    END IF;
+
+    RETURN chain;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+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_use_last    BOOL = false;
+    org_age_is_min  BOOL = false;
+    org_keep_count  INT;
+
+    keep_age        INTERVAL;
+
+    target_acp      RECORD;
+    circ_chain_head action.circulation%ROWTYPE;
+    circ_chain_tail action.circulation%ROWTYPE;
+
+    count_purged    INT;
+    num_incomplete  INT;
+
+    last_finished   TIMESTAMP WITH TIME ZONE;
+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;
+
+    SELECT enabled INTO org_use_last FROM config.global_flag WHERE name = 'history.circ.retention_uses_last_finished';
+    SELECT enabled INTO org_age_is_min FROM config.global_flag WHERE name = 'history.circ.retention_age_is_min';
+
+    -- 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
+        -- And, for those, select circs that are finished and older than keep_age
+        FOR circ_chain_head IN
+            -- For reference, the subquery uses a window function to order the circs newest to oldest and number them
+            -- The outer query then uses that information to skip the most recent set the library wants to keep
+            -- End result is we don't care what order they come out in, as they are all potentials for deletion.
+            SELECT ac.* FROM action.circulation ac JOIN (
+              SELECT  rank() OVER (ORDER BY xact_start DESC), ac.id
+                FROM  action.circulation ac
+                WHERE ac.target_copy = target_acp.target_copy
+                  AND ac.parent_circ IS NULL
+                ORDER BY ac.xact_start ) ranked USING (id)
+                WHERE ranked.rank > org_keep_count
+        LOOP
+
+            SELECT * INTO circ_chain_tail FROM action.circ_chain(circ_chain_head.id) ORDER BY xact_start DESC LIMIT 1;
+            SELECT COUNT(CASE WHEN xact_finish IS NULL THEN 1 ELSE NULL END), MAX(xact_finish) INTO num_incomplete, last_finished FROM action.circ_chain(circ_chain_head.id);
+            CONTINUE WHEN circ_chain_tail.xact_finish IS NULL OR num_incomplete > 0;
+
+            IF NOT org_use_last THEN
+                last_finished := circ_chain_tail.xact_finish;
+            END IF;
+
+            -- 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, '2000 years'::INTERVAL );
+            END IF;
+
+            IF org_age_is_min THEN
+                keep_age := GREATEST( keep_age, org_keep_age );
+            END IF;
+
+            CONTINUE WHEN AGE(NOW(), last_finished) < keep_age;
+
+            -- We've passed the purging tests, purge the circ chain starting at the end
+            -- A trigger should auto-purge the rest of the chain.
+            DELETE FROM action.circulation WHERE id = circ_chain_tail.id;
+
+            count_purged := count_purged + 1;
+
+        END LOOP;
+    END LOOP;
+
+    return count_purged;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0799', :eg_version);
+
+-- allow state to be null
+ALTER TABLE actor.usr_address ALTER COLUMN state DROP NOT NULL;
+
+-- create new YAOUS
+INSERT into config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'ui.patron.edit.au.state.require',
+        'gui',
+        oils_i18n_gettext(
+            'ui.patron.edit.au.state.require',
+            'Require State field on patron registration',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'ui.patron.edit.au.state.require',
+            'The State field will be required on the patron registration screen.',
+            'coust',
+            'description'
+        ),
+        'bool'
+    );
+
+INSERT into config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'ui.patron.edit.au.state.show',
+        'gui',
+        oils_i18n_gettext(
+            'ui.patron.edit.au.state.show',
+            'Show State field on patron registration',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'ui.patron.edit.au.state.show',
+            'The State field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.',
+            'coust',
+            'description'
+        ),
+        'bool'
+    ); 
+
+INSERT into config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'ui.patron.edit.au.state.suggest',
+        'gui',
+        oils_i18n_gettext(
+            'ui.patron.edit.au.state.suggest',
+            'Suggest State field on patron registration',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'ui.patron.edit.au.state.suggest',
+            'The State field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.',
+            'coust',
+            'description'
+        ),
+        'bool'
+    );         
+
+SELECT evergreen.upgrade_deps_block_check('0800', :eg_version);
+
+CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
+DECLARE
+    fclass          RECORD;
+    ind_data        metabib.field_entry_template%ROWTYPE;
+    mbe_row         metabib.browse_entry%ROWTYPE;
+    mbe_id          BIGINT;
+    b_skip_facet    BOOL;
+    b_skip_browse   BOOL;
+    b_skip_search   BOOL;
+BEGIN
+
+    SELECT COALESCE(NULLIF(skip_facet, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_facet_indexing' AND enabled)) INTO b_skip_facet;
+    SELECT COALESCE(NULLIF(skip_browse, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_browse_indexing' AND enabled)) INTO b_skip_browse;
+    SELECT COALESCE(NULLIF(skip_search, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_search_indexing' AND enabled)) INTO b_skip_search;
+
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        IF NOT b_skip_search 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;
+        END IF;
+        IF NOT b_skip_facet THEN
+            DELETE FROM metabib.facet_entry WHERE source = bib_id;
+        END IF;
+        IF NOT b_skip_browse THEN
+            DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
+        END IF;
+    END IF;
+
+    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;
+        END IF;
+
+        IF ind_data.facet_field AND NOT b_skip_facet THEN
+            INSERT INTO metabib.facet_entry (field, source, value)
+                VALUES (ind_data.field, ind_data.source, ind_data.value);
+        END IF;
+
+        IF ind_data.browse_field AND NOT b_skip_browse THEN
+            -- A caveat about this SELECT: this should take care of replacing
+            -- old mbe rows when data changes, but not if normalization (by
+            -- which I mean specifically the output of
+            -- evergreen.oils_tsearch2()) changes.  It may or may not be
+            -- expensive to add a comparison of index_vector to index_vector
+            -- to the WHERE clause below.
+            SELECT INTO mbe_row * FROM metabib.browse_entry WHERE value = ind_data.value;
+            IF FOUND THEN
+                mbe_id := mbe_row.id;
+            ELSE
+                INSERT INTO metabib.browse_entry (value) VALUES
+                    (metabib.browse_normalize(ind_data.value, ind_data.field));
+                mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
+            END IF;
+
+            INSERT INTO metabib.browse_entry_def_map (entry, def, source)
+                VALUES (mbe_id, ind_data.field, ind_data.source);
+        END IF;
+
+        -- Avoid inserting duplicate rows, but retain granularity of being
+        -- able to search browse fields with "starts with" type operators
+        -- (for example, for titles of songs in music albums)
+        IF (ind_data.search_field OR ind_data.browse_field) AND NOT b_skip_search THEN
+            EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
+                '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
+                INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
+                -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
+            IF mbe_id IS NULL THEN
+                EXECUTE $$
+                INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
+                    VALUES ($$ ||
+                        quote_literal(ind_data.field) || $$, $$ ||
+                        quote_literal(ind_data.source) || $$, $$ ||
+                        quote_literal(ind_data.value) ||
+                    $$);$$;
+            END IF;
+        END IF;
+
+    END LOOP;
+
+    IF NOT b_skip_search THEN
+        PERFORM metabib.update_combined_index_vectors(bib_id);
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION public.oils_tsearch2 () RETURNS TRIGGER AS $$
+DECLARE
+    normalizer      RECORD;
+    value           TEXT := '';
+    temp_vector     TEXT := '';
+    ts_rec          RECORD;
+    cur_weight      "char";
+BEGIN
+
+    value := NEW.value;
+    NEW.index_vector = ''::tsvector;
+
+    IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
+              WHERE field = NEW.field AND m.pos < 0
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_literal( value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO value;
+
+        END LOOP;
+
+        NEW.value = value;
+
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
+              WHERE field = NEW.field AND m.pos >= 0
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_literal( value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO value;
+
+        END LOOP;
+   END IF;
+
+    IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
+        value :=  ARRAY_TO_STRING(
+            evergreen.regexp_split_to_array(value, E'\\W+'), ' '
+        );
+        value := public.search_normalize(value);
+        NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value);
+    ELSIF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
+        FOR ts_rec IN
+            SELECT ts_config, index_weight
+            FROM config.metabib_class_ts_map
+            WHERE field_class = TG_ARGV[0]
+                AND index_lang IS NULL OR EXISTS (SELECT 1 FROM metabib.record_attr WHERE id = NEW.source AND index_lang IN(attrs->'item_lang',attrs->'language'))
+                AND always OR NOT EXISTS (SELECT 1 FROM config.metabib_field_ts_map WHERE metabib_field = NEW.field)
+            UNION
+            SELECT ts_config, index_weight
+            FROM config.metabib_field_ts_map
+            WHERE metabib_field = NEW.field
+               AND index_lang IS NULL OR EXISTS (SELECT 1 FROM metabib.record_attr WHERE id = NEW.source AND index_lang IN(attrs->'item_lang',attrs->'language'))
+            ORDER BY index_weight ASC
+        LOOP
+            IF cur_weight IS NOT NULL AND cur_weight != ts_rec.index_weight THEN
+                NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight);
+                temp_vector = '';
+            END IF;
+            cur_weight = ts_rec.index_weight;
+            SELECT INTO temp_vector temp_vector || ' ' || to_tsvector(ts_rec.ts_config::regconfig, value)::TEXT;
+        END LOOP;
+        NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight);
+    ELSE
+        NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value);
+    END IF;
+
+    RETURN NEW;
+END;
+$$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0801', :eg_version);
+
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+( 'ui.patron.edit.ac.barcode.regex', 'gui',
+    oils_i18n_gettext('ui.patron.edit.ac.barcode.regex',
+        'Regex for barcodes on patron registration',
+        'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.ac.barcode.regex',
+        'The Regular Expression for validation on barcodes in patron registration.',
+        'coust', 'description'),
+    'string', null);
+
+SELECT evergreen.upgrade_deps_block_check('0802', :eg_version);
+
+CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
+DECLARE
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    sf_node         TEXT;
+    tag_node        TEXT;
+    thes_code       TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    tmp_text        TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT;
+BEGIN
+    SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    IF cset IS NULL THEN
+        SELECT  control_set INTO cset
+          FROM  authority.control_set_authority_field
+          WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
+          LIMIT 1;
+    END IF;
+
+    thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
+    IF thes_code IS NULL THEN
+        thes_code := '|';
+    ELSIF thes_code = 'z' THEN
+        thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
+    END IF;
+
+    heading_text := '';
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+        first_sf := TRUE;
+
+        FOR tag_node IN SELECT unnest(oils_xpath('//*[@tag="'||tag_used||'"]',marcxml)) LOOP
+            FOR sf_node IN SELECT unnest(oils_xpath('//*[contains("'||acsaf.sf_list||'",@code)]',tag_node)) LOOP
+
+                tmp_text := oils_xpath_string('.', sf_node);
+                sf := oils_xpath_string('./@code', sf_node);
+
+                IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
+
+                    tmp_text := SUBSTRING(
+                        tmp_text FROM
+                        COALESCE(
+                            NULLIF(
+                                REGEXP_REPLACE(
+                                    oils_xpath_string('./@ind'||nfi_used, tag_node),
+                                    $$\D+$$,
+                                    '',
+                                    'g'
+                                ),
+                                ''
+                            )::INT,
+                            0
+                        ) + 1
+                    );
+
+                END IF;
+
+                first_sf := FALSE;
+
+                IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
+                    heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
+                END IF;
+            END LOOP;
+
+            EXIT WHEN heading_text <> '';
+        END LOOP;
+
+        EXIT WHEN heading_text <> '';
+    END LOOP;
+
+    IF heading_text <> '' THEN
+        IF no_thesaurus IS TRUE THEN
+            heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
+        ELSE
+            heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
+        END IF;
+    ELSE
+        heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
+    END IF;
+
+    RETURN heading_text;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
+DECLARE
+    res             authority.simple_heading%ROWTYPE;
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    sort_text       TEXT;
+    tmp_text        TEXT;
+    tmp_xml         TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
+BEGIN
+
+    res.record := auth_id;
+
+    SELECT  control_set INTO cset
+      FROM  authority.control_set_authority_field
+      WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]) )
+      LIMIT 1;
+
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
+
+        res.atag := acsaf.id;
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+
+        FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
+
+            heading_text := public.naco_normalize(
+                COALESCE(
+                    oils_xpath_string('//*[contains("'||acsaf.sf_list||'",@code)]',tmp_xml::TEXT, ' '),
+                    ''
+                )
+            );
+
+            IF nfi_used IS NOT NULL THEN
+
+                sort_text := SUBSTRING(
+                    heading_text FROM
+                    COALESCE(
+                        NULLIF(
+                            REGEXP_REPLACE(
+                                oils_xpath_string('./@ind'||nfi_used, tmp_xml::TEXT),
+                                $$\D+$$,
+                                '',
+                                'g'
+                            ),
+                            ''
+                        )::INT,
+                        0
+                    ) + 1
+                );
+
+            ELSE
+                sort_text := heading_text;
+            END IF;
+
+            IF heading_text IS NOT NULL AND heading_text <> '' THEN
+                res.value := heading_text;
+                res.sort_value := sort_text;
+                RETURN NEXT res;
+            END IF;
+
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0803', :eg_version);
+
+UPDATE config.org_unit_setting_type 
+SET description = oils_i18n_gettext('circ.holds.default_shelf_expire_interval',
+        'The amount of time an item will be held on the shelf before the hold expires. For example: "2 weeks" or "5 days"',
+        'coust', 'description')
+WHERE name = 'circ.holds.default_shelf_expire_interval';
+
+SELECT evergreen.upgrade_deps_block_check('0804', :eg_version);
+
+UPDATE config.coded_value_map
+SET value = oils_i18n_gettext('169', 'Gwich''in', 'ccvm', 'value')
+WHERE ctype = 'item_lang' AND code = 'gwi';
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0805', :eg_version);
+
+INSERT INTO config.global_flag (name, label, enabled)
+    VALUES (
+        'circ.desk_renewal.use_original_circ_lib',
+        oils_i18n_gettext(
+            'circ.desk_renewal.use_original_circ_lib',
+            'Circ: Use original circulation library on desk renewal instead of user home library',
+            'cgf',
+            'label'
+        ),
+        FALSE
+    );
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0806', :eg_version);
+
+INSERT INTO action.hold_request_cancel_cause (id,label) 
+    VALUES (7,'Patron via SIP');
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0807', :eg_version);
+
+ALTER TABLE config.usr_setting_type
+    ADD COLUMN reg_default TEXT;
+
+SELECT evergreen.upgrade_deps_block_check('0808', :eg_version);
+
+CREATE INDEX actor_usr_usrname_idx ON actor.usr (evergreen.lowercase(usrname));
+
+SELECT evergreen.upgrade_deps_block_check('0809', :eg_version);
+
+ALTER TABLE actor.org_address ALTER COLUMN state DROP NOT NULL;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0810', :eg_version);
+
+UPDATE authority.control_set_authority_field
+    SET name = REGEXP_REPLACE(name, '^See Also', 'See From')
+    WHERE tag LIKE '4__' AND control_set = 1;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0811', :eg_version);
+
+DROP FUNCTION action.copy_related_hold_stats(integer);
+
+CREATE OR REPLACE FUNCTION action.copy_related_hold_stats(copy_id bigint)
+  RETURNS action.hold_stats AS
+$BODY$
+DECLARE
+    output          action.hold_stats%ROWTYPE;
+    hold_count      INT := 0;
+    copy_count      INT := 0;
+    available_count INT := 0;
+    hold_map_data   RECORD;
+BEGIN
+
+    output.hold_count := 0;
+    output.copy_count := 0;
+    output.available_count := 0;
+
+    SELECT  COUNT( DISTINCT m.hold ) INTO hold_count
+      FROM  action.hold_copy_map m
+            JOIN action.hold_request h ON (m.hold = h.id)
+      WHERE m.target_copy = copy_id
+            AND NOT h.frozen;
+
+    output.hold_count := hold_count;
+
+    IF output.hold_count > 0 THEN
+        FOR hold_map_data IN
+            SELECT  DISTINCT m.target_copy,
+                    acp.status
+              FROM  action.hold_copy_map m
+                    JOIN asset.copy acp ON (m.target_copy = acp.id)
+                    JOIN action.hold_request h ON (m.hold = h.id)
+              WHERE m.hold IN ( SELECT DISTINCT hold FROM action.hold_copy_map WHERE target_copy = copy_id ) AND NOT h.frozen
+        LOOP
+            output.copy_count := output.copy_count + 1;
+            IF hold_map_data.status IN (0,7,12) THEN
+                output.available_count := output.available_count + 1;
+            END IF;
+        END LOOP;
+        output.total_copy_ratio = output.copy_count::FLOAT / output.hold_count::FLOAT;
+        output.available_copy_ratio = output.available_count::FLOAT / output.hold_count::FLOAT;
+
+    END IF;
+
+    RETURN output;
+
+END;
+$BODY$
+  LANGUAGE plpgsql VOLATILE
+  COST 100;
+
+
+-- Evergreen DB patch 0812.data.add_library_info_url_OUS.sql
+--
+-- Adds YAOUS for enabling information links from the TPAC to a library URL
+--
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0812', :eg_version);
+
+-- FIXME: add/check SQL statements to perform the upgrade
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+( 'lib.info_url', 'lib',
+    oils_i18n_gettext('lib.info_url',
+        'Library information URL (such as "http://example.com/about.html")',
+        'coust', 'label'),
+    oils_i18n_gettext('lib.info_url',
+        'URL for information on this library, such as contact information, hours of operation, and directions. If set, the library name in the copy details section links to that URL. Use a complete URL, such as "http://example.com/hours.html".',
+        'coust', 'description'),
+    'string', null)
+;
+
+SELECT evergreen.upgrade_deps_block_check('0813', :eg_version);
+
+-- Don't require state in the auditor tracking for user addresses
+
+ALTER TABLE auditor.actor_usr_address_history ALTER COLUMN state DROP NOT NULL;
+
+-- Change constraint on actor.org_unit_setting_log to be deferrable initially
+
+ALTER TABLE config.org_unit_setting_type_log
+  DROP CONSTRAINT org_unit_setting_type_log_field_name_fkey,
+  ADD CONSTRAINT org_unit_setting_type_log_field_name_fkey FOREIGN KEY (field_name)
+    REFERENCES config.org_unit_setting_type (name) MATCH SIMPLE
+    ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED;
+
+-- Fix names in the org unit setting configuration
+
+UPDATE config.org_unit_setting_type SET name = overlay(name placing 'aua' from 16 for 2) where name like 'ui.patron.edit.au.state.%';
+
+-- Fix names if they have already been set in the editor
+
+UPDATE actor.org_unit_setting SET name = overlay(name placing 'aua' from 16 for 2) where name like 'ui.patron.edit.au.state.%';
+
+-- and the logs too
+
+UPDATE config.org_unit_setting_type_log SET field_name = overlay(field_name placing 'aua' from 16 for 2) where field_name like 'ui.patron.edit.au.state.%';
+
+SELECT evergreen.upgrade_deps_block_check('0814', :eg_version);
+
+UPDATE permission.perm_list
+SET description = 'Allow a user to delete a provider'
+WHERE code = 'DELETE_PROVIDER';
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0815', :eg_version);
+
+ALTER TABLE authority.control_set_authority_field
+    ADD COLUMN linking_subfield CHAR(1);
+
+UPDATE authority.control_set_authority_field
+    SET linking_subfield = '0' WHERE main_entry IS NOT NULL;
+
+CREATE TABLE authority.authority_linking (
+    id      BIGSERIAL PRIMARY KEY,
+    source  BIGINT REFERENCES authority.record_entry (id) NOT NULL,
+    target  BIGINT REFERENCES authority.record_entry (id) NOT NULL,
+    field   INT REFERENCES authority.control_set_authority_field (id) NOT NULL
+);
+
+-- Given an authority record's ID, control set ID (if known), and marc::XML,
+-- return all links to other authority records in the form of rows that
+-- can be inserted into authority.authority_linking.
+CREATE OR REPLACE FUNCTION authority.calculate_authority_linking(
+    rec_id BIGINT, rec_control_set INT, rec_marc_xml XML
+) RETURNS SETOF authority.authority_linking AS $func$
+DECLARE
+    acsaf       authority.control_set_authority_field%ROWTYPE;
+    link        TEXT;
+    aal         authority.authority_linking%ROWTYPE;
+BEGIN
+    IF rec_control_set IS NULL THEN
+        -- No control_set on record?  Guess at one
+        SELECT control_set INTO rec_control_set
+            FROM authority.control_set_authority_field
+            WHERE tag IN (
+                SELECT UNNEST(
+                    XPATH('//*[starts-with(@tag,"1")]/@tag',rec_marc_xml::XML)::TEXT[]
+                )
+            ) LIMIT 1;
+
+        IF NOT FOUND THEN
+            RAISE WARNING 'Could not even guess at control set for authority record %', rec_id;
+            RETURN;
+        END IF;
+    END IF;
+
+    aal.source := rec_id;
+
+    FOR acsaf IN
+        SELECT * FROM authority.control_set_authority_field
+        WHERE control_set = rec_control_set
+            AND linking_subfield IS NOT NULL
+            AND main_entry IS NOT NULL
+    LOOP
+        link := SUBSTRING(
+            (XPATH('//*[@tag="' || acsaf.tag || '"]/*[@code="' ||
+                acsaf.linking_subfield || '"]/text()', rec_marc_xml))[1]::TEXT,
+            '\d+$'
+        );
+
+        -- Ignore links that are null, malformed, circular, or point to
+        -- non-existent authority records.
+        IF link IS NOT NULL AND link::BIGINT <> rec_id THEN
+            PERFORM * FROM authority.record_entry WHERE id = link::BIGINT;
+            IF FOUND THEN
+                aal.target := link::BIGINT;
+                aal.field := acsaf.id;
+                RETURN NEXT aal;
+            END IF;
+        END IF;
+    END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+-- AFTER UPDATE OR INSERT trigger for authority.record_entry
+CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+BEGIN
+
+    IF NEW.deleted IS TRUE THEN -- If this authority is deleted
+        DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
+          -- Should remove matching $0 from controlled fields at the same time?
+
+        -- XXX What do we about the actual linking subfields present in
+        -- authority records that target this one when this happens?
+        DELETE FROM authority.authority_linking
+            WHERE source = NEW.id OR target = NEW.id;
+
+        RETURN NEW; -- and we're done
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+
+        -- Propagate these updates to any linked bib records
+        PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
+
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
+        DELETE FROM authority.authority_linking WHERE source = NEW.id;
+    END IF;
+
+    INSERT INTO authority.authority_linking (source, target, field)
+        SELECT source, target, field FROM authority.calculate_authority_linking(
+            NEW.id, NEW.control_set, NEW.marc::XML
+        );
+
+    INSERT INTO authority.simple_heading (record,atag,value,sort_value)
+        SELECT record, atag, value, sort_value FROM authority.simple_heading_set(NEW.marc);
+
+    -- Flatten and insert the afr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM authority.reingest_authority_full_rec(NEW.id);
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
+        END IF;
+    END IF;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+COMMIT;
+BEGIN;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0816', :eg_version);
+
+-- To avoid problems with altering a table column after doing an
+-- update.
+ALTER TABLE authority.control_set_authority_field
+    DISABLE TRIGGER ALL;
+
+ALTER TABLE authority.control_set_authority_field
+    ADD COLUMN display_sf_list TEXT;
+
+UPDATE authority.control_set_authority_field
+    SET display_sf_list = REGEXP_REPLACE(sf_list, '[w254]', '', 'g');
+
+ALTER TABLE authority.control_set_authority_field
+    ALTER COLUMN display_sf_list SET NOT NULL;
+
+ALTER TABLE authority.control_set_authority_field
+    ENABLE TRIGGER ALL;
+
+ALTER TABLE metabib.browse_entry_def_map
+    ADD COLUMN authority BIGINT REFERENCES authority.record_entry (id)
+        ON DELETE SET NULL;
+
+ALTER TABLE config.metabib_field ADD COLUMN authority_xpath TEXT;
+ALTER TABLE config.metabib_field ADD COLUMN browse_sort_xpath TEXT;
+
+UPDATE config.metabib_field
+    SET authority_xpath = '//@xlink:href'
+    WHERE
+        format = 'mods32' AND
+        field_class IN ('subject','series','title','author') AND
+        browse_field IS TRUE;
+
+ALTER TYPE metabib.field_entry_template ADD ATTRIBUTE authority BIGINT;
+ALTER TYPE metabib.field_entry_template ADD ATTRIBUTE sort_value TEXT;
+
+CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
+DECLARE
+    fclass          RECORD;
+    ind_data        metabib.field_entry_template%ROWTYPE;
+    mbe_row         metabib.browse_entry%ROWTYPE;
+    mbe_id          BIGINT;
+    b_skip_facet    BOOL;
+    b_skip_browse   BOOL;
+    b_skip_search   BOOL;
+    value_prepped   TEXT;
+BEGIN
+
+    SELECT COALESCE(NULLIF(skip_facet, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_facet_indexing' AND enabled)) INTO b_skip_facet;
+    SELECT COALESCE(NULLIF(skip_browse, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_browse_indexing' AND enabled)) INTO b_skip_browse;
+    SELECT COALESCE(NULLIF(skip_search, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_search_indexing' AND enabled)) INTO b_skip_search;
+
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        IF NOT b_skip_search 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;
+        END IF;
+        IF NOT b_skip_facet THEN
+            DELETE FROM metabib.facet_entry WHERE source = bib_id;
+        END IF;
+        IF NOT b_skip_browse THEN
+            DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
+        END IF;
+    END IF;
+
+    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;
+        END IF;
+
+        IF ind_data.facet_field AND NOT b_skip_facet THEN
+            INSERT INTO metabib.facet_entry (field, source, value)
+                VALUES (ind_data.field, ind_data.source, ind_data.value);
+        END IF;
+
+        IF ind_data.browse_field AND NOT b_skip_browse THEN
+            -- A caveat about this SELECT: this should take care of replacing
+            -- old mbe rows when data changes, but not if normalization (by
+            -- which I mean specifically the output of
+            -- evergreen.oils_tsearch2()) changes.  It may or may not be
+            -- expensive to add a comparison of index_vector to index_vector
+            -- to the WHERE clause below.
+
+            value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
+            SELECT INTO mbe_row * FROM metabib.browse_entry
+                WHERE value = value_prepped AND sort_value = ind_data.sort_value;
+
+            IF FOUND THEN
+                mbe_id := mbe_row.id;
+            ELSE
+                INSERT INTO metabib.browse_entry
+                    ( value, sort_value ) VALUES
+                    ( value_prepped, ind_data.sort_value );
+
+                mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
+            END IF;
+
+            INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
+                VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
+        END IF;
+
+        IF ind_data.search_field AND NOT b_skip_search THEN
+            EXECUTE $$
+                INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
+                    VALUES ($$ ||
+                        quote_literal(ind_data.field) || $$, $$ ||
+                        quote_literal(ind_data.source) || $$, $$ ||
+                        quote_literal(ind_data.value) ||
+                    $$);$$;
+        END IF;
+
+    END LOOP;
+
+    IF NOT b_skip_search THEN
+        PERFORM metabib.update_combined_index_vectors(bib_id);
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
+DECLARE
+    bib     biblio.record_entry%ROWTYPE;
+    idx     config.metabib_field%ROWTYPE;
+    xfrm        config.xml_transform%ROWTYPE;
+    prev_xfrm   TEXT;
+    transformed_xml TEXT;
+    xml_node    TEXT;
+    xml_node_list   TEXT[];
+    facet_text  TEXT;
+    browse_text TEXT;
+    sort_value  TEXT;
+    raw_text    TEXT;
+    curr_text   TEXT;
+    joiner      TEXT := default_joiner; -- XXX will index defs supply a joiner?
+    authority_text TEXT;
+    authority_link BIGINT;
+    output_row  metabib.field_entry_template%ROWTYPE;
+BEGIN
+
+    -- Get the record
+    SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
+
+    -- Loop over the indexing entries
+    FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
+
+        SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
+
+        -- See if we can skip the XSLT ... it's expensive
+        IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+            -- Can't skip the transform
+            IF xfrm.xslt <> '---' THEN
+                transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
+            ELSE
+                transformed_xml := bib.marc;
+            END IF;
+
+            prev_xfrm := xfrm.name;
+        END IF;
+
+        xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+
+        raw_text := NULL;
+        FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
+            CONTINUE WHEN xml_node !~ E'^\\s*<';
+
+            curr_text := ARRAY_TO_STRING(
+                oils_xpath( '//text()',
+                    REGEXP_REPLACE( -- This escapes all &s not followed by "amp;".  Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
+                        REGEXP_REPLACE( -- This escapes embeded <s
+                            xml_node,
+                            $re$(>[^<]+)(<)([^>]+<)$re$,
+                            E'\\1&lt;\\3',
+                            'g'
+                        ),
+                        '&(?!amp;)',
+                        '&amp;',
+                        'g'
+                    )
+                ),
+                ' '
+            );
+
+            CONTINUE WHEN curr_text IS NULL OR curr_text = '';
+
+            IF raw_text IS NOT NULL THEN
+                raw_text := raw_text || joiner;
+            END IF;
+
+            raw_text := COALESCE(raw_text,'') || curr_text;
+
+            -- autosuggest/metabib.browse_entry
+            IF idx.browse_field THEN
+
+                IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
+                    browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    browse_text := curr_text;
+                END IF;
+
+                IF idx.browse_sort_xpath IS NOT NULL AND
+                    idx.browse_sort_xpath <> '' THEN
+
+                    sort_value := oils_xpath_string(
+                        idx.browse_sort_xpath, xml_node, joiner,
+                        ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
+                    );
+                ELSE
+                    sort_value := browse_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
+                output_row.sort_value :=
+                    public.search_normalize(sort_value);
+
+                output_row.authority := NULL;
+
+                IF idx.authority_xpath IS NOT NULL AND idx.authority_xpath <> '' THEN
+                    authority_text := oils_xpath_string(
+                        idx.authority_xpath, xml_node, joiner,
+                        ARRAY[
+                            ARRAY[xfrm.prefix, xfrm.namespace_uri],
+                            ARRAY['xlink','http://www.w3.org/1999/xlink']
+                        ]
+                    );
+
+                    IF authority_text ~ '^\d+$' THEN
+                        authority_link := authority_text::BIGINT;
+                        PERFORM * FROM authority.record_entry WHERE id = authority_link;
+                        IF FOUND THEN
+                            output_row.authority := authority_link;
+                        END IF;
+                    END IF;
+
+                END IF;
+
+                output_row.browse_field = TRUE;
+                RETURN NEXT output_row;
+                output_row.browse_field = FALSE;
+                output_row.sort_value := NULL;
+            END IF;
+
+            -- insert raw node text for faceting
+            IF idx.facet_field THEN
+
+                IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
+                    facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    facet_text := curr_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = -1 * idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
+
+                output_row.facet_field = TRUE;
+                RETURN NEXT output_row;
+                output_row.facet_field = FALSE;
+            END IF;
+
+        END LOOP;
+
+        CONTINUE WHEN raw_text IS NULL OR raw_text = '';
+
+        -- insert combined node text for searching
+        IF idx.search_field THEN
+            output_row.field_class = idx.field_class;
+            output_row.field = idx.id;
+            output_row.source = rid;
+            output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
+
+            output_row.search_field = TRUE;
+            RETURN NEXT output_row;
+            output_row.search_field = FALSE;
+        END IF;
+
+    END LOOP;
+
+END;
+
+$func$ LANGUAGE PLPGSQL;
+
+
+-- 953.data.MODS32-xsl.sql
+UPDATE config.xml_transform SET xslt=$$<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns="http://www.loc.gov/mods/v3" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xlink marc" version="1.0">
+       <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
+<!--
+Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements 
+  with corresponding @type and @invalid eq 'yes' when subfields z or y (in the case of 022) exist in the MARCXML ::: 2007/01/04 17:35:20 cred
+
+Revision 1.13 - Changed order of output under cartographics to reflect schema  2006/11/28 tmee
+       
+Revision 1.12 - Updated to reflect MODS 3.2 Mapping  2006/10/11 tmee
+               
+Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>
+      2006/04/08  jrad
+
+Revision 1.10 MODS 3.1 revisions to language and classification elements  
+                               (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)
+                               2006/02/06  ggar
+
+Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
+
+Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
+
+Revision 1.7 2004/03/25 08:29 jrad
+
+Revision 1.6 various validation fixes 2004/02/20 ntra
+
+Revision 1.5  2003/10/02 16:18:58  ntra
+MODS2 to MODS3 updates, language unstacking and 
+de-duping, chopPunctuation expanded
+
+Revision 1.3  2003/04/03 00:07:19  ntra
+Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
+
+Revision 1.2  2003/03/24 19:37:42  ckeith
+Added Log Comment
+
+-->
+       <xsl:template match="/">
+               <xsl:choose>
+                       <xsl:when test="//marc:collection">
+                               <modsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
+                                       <xsl:for-each select="//marc:collection/marc:record">
+                                               <mods version="3.2">
+                                                       <xsl:call-template name="marcRecord"/>
+                                               </mods>
+                                       </xsl:for-each>
+                               </modsCollection>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
+                                       <xsl:for-each select="//marc:record">
+                                               <xsl:call-template name="marcRecord"/>
+                                       </xsl:for-each>
+                               </mods>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="marcRecord">
+               <xsl:variable name="leader" select="marc:leader"/>
+               <xsl:variable name="leader6" select="substring($leader,7,1)"/>
+               <xsl:variable name="leader7" select="substring($leader,8,1)"/>
+               <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
+               <xsl:variable name="typeOf008">
+                       <xsl:choose>
+                               <xsl:when test="$leader6='a'">
+                                       <xsl:choose>
+                                               <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
+                                               <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
+                                       </xsl:choose>
+                               </xsl:when>
+                               <xsl:when test="$leader6='t'">BK</xsl:when>
+                               <xsl:when test="$leader6='p'">MM</xsl:when>
+                               <xsl:when test="$leader6='m'">CF</xsl:when>
+                               <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
+                               <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
+                               <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'">MU</xsl:when>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:for-each select="marc:datafield[@tag='245']">
+                       <titleInfo>
+                               <xsl:variable name="title">
+                                       <xsl:choose>
+                                               <xsl:when test="marc:subfield[@code='b']">
+                                                       <xsl:call-template name="specialSubfieldSelect">
+                                                               <xsl:with-param name="axis">b</xsl:with-param>
+                                                               <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">abfgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                               </xsl:variable>
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="$title"/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <xsl:choose>
+                                       <xsl:when test="@ind2>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop"/>
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:if test="marc:subfield[@code='b']">
+                                       <subTitle>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="axis">b</xsl:with-param>
+                                                                       <xsl:with-param name="anyCodes">b</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">afgk</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </subTitle>
+                               </xsl:if>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleInfo>
+                       <!-- A form of title that ignores non-filing characters; useful
+                                for not converting "L'Oreal" into "L' Oreal" at index time -->
+                       <titleNonfiling>
+                               <xsl:variable name="title">
+                                       <xsl:choose>
+                                               <xsl:when test="marc:subfield[@code='b']">
+                                                       <xsl:call-template name="specialSubfieldSelect">
+                                                               <xsl:with-param name="axis">b</xsl:with-param>
+                                                               <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">abfgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                               </xsl:variable>
+                               <title>
+                                       <xsl:value-of select="$title"/>
+                               </title>
+                               <xsl:if test="marc:subfield[@code='b']">
+                                       <subTitle>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="axis">b</xsl:with-param>
+                                                                       <xsl:with-param name="anyCodes">b</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">afgk</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </subTitle>
+                               </xsl:if>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleNonfiling>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='210']">
+                       <titleInfo type="abbreviated">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">a</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="subtitle"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='242']">
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <!-- 1/04 removed $h, b -->
+                                                       <xsl:with-param name="codes">a</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo type="translated">
+                               <!--09/01/04 Added subfield $y-->
+                               <xsl:for-each select="marc:subfield[@code='y']">
+                                       <xsl:attribute name="lang">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <title>
+                                       <xsl:value-of select="$titleChop" />
+                               </title>
+                               <!-- 1/04 fix -->
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+                       <titleInfo type="translated-nfi">
+                               <xsl:for-each select="marc:subfield[@code='y']">
+                                       <xsl:attribute name="lang">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <xsl:choose>
+                                       <xsl:when test="@ind2>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop" />
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='246']">
+                       <titleInfo type="alternative">
+                               <xsl:for-each select="marc:subfield[@code='i']">
+                                       <xsl:attribute name="displayLabel">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <!-- 1/04 removed $h, $b -->
+                                                               <xsl:with-param name="codes">af</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
+                       <xsl:variable name="nfi">
+                               <xsl:choose>
+                                       <xsl:when test="@tag='240'">
+                                               <xsl:value-of select="@ind2"/>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:value-of select="@ind1"/>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </xsl:variable>
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="uri" />
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield">
+                                               <xsl:if test="(contains('adfklmor',@code) and (not(../marc:subfield[@code='n' or @code='p']) or (following-sibling::marc:subfield[@code='n' or @code='p'])))">
+                                                       <xsl:value-of select="text()"/>
+                                                       <xsl:text> </xsl:text>
+                                               </xsl:if>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo type="uniform">
+                               <title>
+                                       <xsl:value-of select="$titleChop"/>
+                               </title>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+                       <titleInfo type="uniform-nfi">
+                               <xsl:choose>
+                                       <xsl:when test="$nfi>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,$nfi)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,$nfi+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop"/>
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">ah</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo type="alternative">
+                               <title>
+                                       <xsl:value-of select="$titleChop" />
+                               </title>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+                       <titleInfo type="alternative-nfi">
+                               <xsl:choose>
+                                       <xsl:when test="@ind1>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind1)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind1+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop" />
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='100']">
+                       <name type="personal">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDQ"/>
+                               <xsl:call-template name="affiliation"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='110']">
+                       <name type="corporate">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDN"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='111']">
+                       <name type="conference">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameACDEQ"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
+                       <name type="personal">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDQ"/>
+                               <xsl:call-template name="affiliation"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
+                       <name type="corporate">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDN"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
+                       <name type="conference">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameACDEQ"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
+                       <name>
+                               <xsl:if test="@ind1=1">
+                                       <xsl:attribute name="type">
+                                               <xsl:text>personal</xsl:text>
+                                       </xsl:attribute>
+                               </xsl:if>
+                               <namePart>
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </namePart>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <typeOfResource>
+                       <xsl:if test="$leader7='c'">
+                               <xsl:attribute name="collection">yes</xsl:attribute>
+                       </xsl:if>
+                       <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
+                               <xsl:attribute name="manuscript">yes</xsl:attribute>
+                       </xsl:if>
+                       <xsl:choose>
+                               <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
+                               <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
+                               <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
+                               <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
+                               <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
+                               <xsl:when test="$leader6='k'">still image</xsl:when>
+                               <xsl:when test="$leader6='g'">moving image</xsl:when>
+                               <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
+                               <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
+                               <xsl:when test="$leader6='p'">mixed material</xsl:when>
+                       </xsl:choose>
+               </typeOfResource>
+               <xsl:if test="substring($controlField008,26,1)='d'">
+                       <genre authority="marc">globe</genre>
+               </xsl:if>
+               <xsl:if test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
+                       <genre authority="marc">remote sensing image</genre>
+               </xsl:if>
+               <xsl:if test="$typeOf008='MP'">
+                       <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-25='a' or $controlField008-25='b' or $controlField008-25='c' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
+                                       <genre authority="marc">map</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
+                                       <genre authority="marc">atlas</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='SE'">
+                       <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-21='d'">
+                                       <genre authority="marc">database</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='l'">
+                                       <genre authority="marc">loose-leaf</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='m'">
+                                       <genre authority="marc">series</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='n'">
+                                       <genre authority="marc">newspaper</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='p'">
+                                       <genre authority="marc">periodical</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='w'">
+                                       <genre authority="marc">web site</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
+                       <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="contains($controlField008-24,'a')">
+                                       <genre authority="marc">abstract or summary</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'b')">
+                                       <genre authority="marc">bibliography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'c')">
+                                       <genre authority="marc">catalog</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'d')">
+                                       <genre authority="marc">dictionary</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'e')">
+                                       <genre authority="marc">encyclopedia</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'f')">
+                                       <genre authority="marc">handbook</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'g')">
+                                       <genre authority="marc">legal article</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'i')">
+                                       <genre authority="marc">index</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'k')">
+                                       <genre authority="marc">discography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'l')">
+                                       <genre authority="marc">legislation</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'m')">
+                                       <genre authority="marc">theses</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'n')">
+                                       <genre authority="marc">survey of literature</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'o')">
+                                       <genre authority="marc">review</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'p')">
+                                       <genre authority="marc">programmed text</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'q')">
+                                       <genre authority="marc">filmography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'r')">
+                                       <genre authority="marc">directory</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'s')">
+                                       <genre authority="marc">statistics</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'t')">
+                                       <genre authority="marc">technical report</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'v')">
+                                       <genre authority="marc">legal case and case notes</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'w')">
+                                       <genre authority="marc">law report or digest</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'z')">
+                                       <genre authority="marc">treaty</genre>
+                               </xsl:when>
+                       </xsl:choose>
+                       <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-29='1'">
+                                       <genre authority="marc">conference publication</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='CF'">
+                       <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-26='a'">
+                                       <genre authority="marc">numeric data</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='e'">
+                                       <genre authority="marc">database</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='f'">
+                                       <genre authority="marc">font</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='g'">
+                                       <genre authority="marc">game</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='BK'">
+                       <xsl:if test="substring($controlField008,25,1)='j'">
+                               <genre authority="marc">patent</genre>
+                       </xsl:if>
+                       <xsl:if test="substring($controlField008,31,1)='1'">
+                               <genre authority="marc">festschrift</genre>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"></xsl:variable>
+                       <xsl:if test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
+                               <genre authority="marc">biography</genre>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-33='e'">
+                                       <genre authority="marc">essay</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='d'">
+                                       <genre authority="marc">drama</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='c'">
+                                       <genre authority="marc">comic strip</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='l'">
+                                       <genre authority="marc">fiction</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='h'">
+                                       <genre authority="marc">humor, satire</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='i'">
+                                       <genre authority="marc">letter</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='f'">
+                                       <genre authority="marc">novel</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='j'">
+                                       <genre authority="marc">short story</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='s'">
+                                       <genre authority="marc">speech</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='MU'">
+                       <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"></xsl:variable>
+                       <xsl:if test="contains($controlField008-30-31,'b')">
+                               <genre authority="marc">biography</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'c')">
+                               <genre authority="marc">conference publication</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'d')">
+                               <genre authority="marc">drama</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'e')">
+                               <genre authority="marc">essay</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'f')">
+                               <genre authority="marc">fiction</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'o')">
+                               <genre authority="marc">folktale</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'h')">
+                               <genre authority="marc">history</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'k')">
+                               <genre authority="marc">humor, satire</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'m')">
+                               <genre authority="marc">memoir</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'p')">
+                               <genre authority="marc">poetry</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'r')">
+                               <genre authority="marc">rehearsal</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'g')">
+                               <genre authority="marc">reporting</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'s')">
+                               <genre authority="marc">sound</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'l')">
+                               <genre authority="marc">speech</genre>
+                       </xsl:if>
+               </xsl:if>
+               <xsl:if test="$typeOf008='VM'">
+                       <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-33='a'">
+                                       <genre authority="marc">art original</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='b'">
+                                       <genre authority="marc">kit</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='c'">
+                                       <genre authority="marc">art reproduction</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='d'">
+                                       <genre authority="marc">diorama</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='f'">
+                                       <genre authority="marc">filmstrip</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='g'">
+                                       <genre authority="marc">legal article</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='i'">
+                                       <genre authority="marc">picture</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='k'">
+                                       <genre authority="marc">graphic</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='l'">
+                                       <genre authority="marc">technical drawing</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='m'">
+                                       <genre authority="marc">motion picture</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='n'">
+                                       <genre authority="marc">chart</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='o'">
+                                       <genre authority="marc">flash card</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='p'">
+                                       <genre authority="marc">microscope slide</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
+                                       <genre authority="marc">model</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='r'">
+                                       <genre authority="marc">realia</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='s'">
+                                       <genre authority="marc">slide</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='t'">
+                                       <genre authority="marc">transparency</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='v'">
+                                       <genre authority="marc">videorecording</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='w'">
+                                       <genre authority="marc">toy</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=655]">
+                       <genre authority="marc">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"/>
+                               </xsl:attribute>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abvxyz</xsl:with-param>
+                                       <xsl:with-param name="delimeter">-</xsl:with-param>
+                               </xsl:call-template>
+                       </genre>
+               </xsl:for-each>
+               <originInfo>
+                       <xsl:variable name="MARCpublicationCode" select="normalize-space(substring($controlField008,16,3))"></xsl:variable>
+                       <xsl:if test="translate($MARCpublicationCode,'|','')">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">code</xsl:attribute>
+                                               <xsl:attribute name="authority">marccountry</xsl:attribute>
+                                               <xsl:value-of select="$MARCpublicationCode"/>
+                                       </placeTerm>
+                               </place>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">code</xsl:attribute>
+                                               <xsl:attribute name="authority">iso3166</xsl:attribute>
+                                               <xsl:value-of select="."/>
+                                       </placeTerm>
+                               </place>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">text</xsl:attribute>
+                                               <xsl:call-template name="chopPunctuationFront">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="chopPunctuation">
+                                                                       <xsl:with-param name="chopString" select="."/>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </placeTerm>
+                               </place>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
+                               <dateValid point="start">
+                                       <xsl:value-of select="."/>
+                               </dateValid>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
+                               <dateValid point="end">
+                                       <xsl:value-of select="."/>
+                               </dateValid>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
+                               <dateModified>
+                                       <xsl:value-of select="."/>
+                               </dateModified>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
+                               <xsl:choose>
+                                       <xsl:when test="@code='b'">
+                                               <publisher>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                               <xsl:with-param name="punctuation">
+                                                                       <xsl:text>:,;/ </xsl:text>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </publisher>
+                                       </xsl:when>
+                                       <xsl:when test="@code='c'">
+                                               <dateIssued>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </dateIssued>
+                                       </xsl:when>
+                                       <xsl:when test="@code='g'">
+                                               <dateCreated>
+                                                       <xsl:value-of select="."/>
+                                               </dateCreated>
+                                       </xsl:when>
+                               </xsl:choose>
+                       </xsl:for-each>
+                       <xsl:variable name="dataField260c">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="marc:datafield[@tag=260]/marc:subfield[@code='c']"></xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <xsl:variable name="controlField008-7-10" select="normalize-space(substring($controlField008, 8, 4))"></xsl:variable>
+                       <xsl:variable name="controlField008-11-14" select="normalize-space(substring($controlField008, 12, 4))"></xsl:variable>
+                       <xsl:variable name="controlField008-6" select="normalize-space(substring($controlField008, 7, 1))"></xsl:variable>
+                       <xsl:if test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
+                               <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
+                                       <dateIssued encoding="marc">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
+                               <xsl:if test="$controlField008-7-10">
+                                       <dateIssued encoding="marc" point="start">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <dateIssued encoding="marc" point="end">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='q'">
+                               <xsl:if test="$controlField008-7-10">
+                                       <dateIssued encoding="marc" point="start" qualifier="questionable">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='q'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <dateIssued encoding="marc" point="end" qualifier="questionable">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='t'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <copyrightDate encoding="marc">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </copyrightDate>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
+                               <dateCaptured encoding="iso8601">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
+                               <dateCaptured encoding="iso8601" point="start">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
+                               <dateCaptured encoding="iso8601" point="end">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
+                               <edition>
+                                       <xsl:value-of select="."/>
+                               </edition>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:leader">
+                               <issuance>
+                                       <xsl:choose>
+                                               <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">monographic</xsl:when>
+                                               <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">continuing</xsl:when>
+                                       </xsl:choose>
+                               </issuance>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
+                               <frequency>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">ab</xsl:with-param>
+                                       </xsl:call-template>
+                               </frequency>
+                       </xsl:for-each>
+               </originInfo>
+               <xsl:variable name="controlField008-35-37" select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"></xsl:variable>
+               <xsl:if test="$controlField008-35-37">
+                       <language>
+                               <languageTerm authority="iso639-2b" type="code">
+                                       <xsl:value-of select="substring($controlField008,36,3)"/>
+                               </languageTerm>
+                       </language>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=041]">
+                       <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='d' or @code='e' or @code='f' or @code='g' or @code='h']">
+                               <xsl:variable name="langCodes" select="."/>
+                               <xsl:choose>
+                                       <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
+                                               <!-- not stacked but could be repeated -->
+                                               <xsl:call-template name="rfcLanguages">
+                                                       <xsl:with-param name="nodeNum">
+                                                               <xsl:value-of select="1"/>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="usedLanguages">
+                                                               <xsl:text></xsl:text>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="controlField008-35-37">
+                                                               <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <!-- iso -->
+                                               <xsl:variable name="allLanguages">
+                                                       <xsl:copy-of select="$langCodes"></xsl:copy-of>
+                                               </xsl:variable>
+                                               <xsl:variable name="currentLanguage">
+                                                       <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
+                                               </xsl:variable>
+                                               <xsl:call-template name="isoLanguage">
+                                                       <xsl:with-param name="currentLanguage">
+                                                               <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="remainingLanguages">
+                                                               <xsl:value-of select="substring($allLanguages,4,string-length($allLanguages)-3)"></xsl:value-of>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="usedLanguages">
+                                                               <xsl:if test="$controlField008-35-37">
+                                                                       <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
+                                                               </xsl:if>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </xsl:for-each>
+               </xsl:for-each>
+               <xsl:variable name="physicalDescription">
+                       <!--3.2 change tmee 007/11 -->
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
+                               <digitalOrigin>reformatted digital</digitalOrigin>
+                       </xsl:if>
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
+                               <digitalOrigin>digitized microfilm</digitalOrigin>
+                       </xsl:if>
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
+                               <digitalOrigin>digitized other analog</digitalOrigin>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"></xsl:variable>
+                       <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
+                       <xsl:variable name="check008-23">
+                               <xsl:if test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
+                                       <xsl:value-of select="true()"></xsl:value-of>
+                               </xsl:if>
+                       </xsl:variable>
+                       <xsl:variable name="check008-29">
+                               <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
+                                       <xsl:value-of select="true()"></xsl:value-of>
+                               </xsl:if>
+                       </xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
+                                       <form authority="marcform">braille</form>
+                               </xsl:when>
+                               <xsl:when test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
+                                       <form authority="marcform">print</form>
+                               </xsl:when>
+                               <xsl:when test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
+                                       <form authority="marcform">electronic</form>
+                               </xsl:when>
+                               <xsl:when test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
+                                       <form authority="marcform">microfiche</form>
+                               </xsl:when>
+                               <xsl:when test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
+                                       <form authority="marcform">microfilm</form>
+                               </xsl:when>
+                       </xsl:choose>
+                       <!-- 1/04 fix -->
+                       <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
+                               <form>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </form>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
+                               <xsl:choose>
+                                       <xsl:when test="substring(text(),14,1)='a'">
+                                               <reformattingQuality>access</reformattingQuality>
+                                       </xsl:when>
+                                       <xsl:when test="substring(text(),14,1)='p'">
+                                               <reformattingQuality>preservation</reformattingQuality>
+                                       </xsl:when>
+                                       <xsl:when test="substring(text(),14,1)='r'">
+                                               <reformattingQuality>replacement</reformattingQuality>
+                                       </xsl:when>
+                               </xsl:choose>
+                       </xsl:for-each>
+                       <!--3.2 change tmee 007/01 -->
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
+                               <form authority="smd">chip cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
+                               <form authority="smd">computer optical disc cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
+                               <form authority="smd">magnetic disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
+                               <form authority="smd">magneto-optical disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
+                               <form authority="smd">optical disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
+                               <form authority="smd">tape cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
+                               <form authority="smd">tape cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
+                               <form authority="smd">tape reel</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
+                               <form authority="smd">celestial globe</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
+                               <form authority="smd">earth moon globe</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
+                               <form authority="smd">planetary or lunar globe</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
+                               <form authority="smd">terrestrial globe</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
+                               <form authority="smd">kit</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
+                               <form authority="smd">atlas</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
+                               <form authority="smd">diagram</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
+                               <form authority="smd">map</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
+                               <form authority="smd">model</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
+                               <form authority="smd">profile</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote-sensing image</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
+                               <form authority="smd">section</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
+                               <form authority="smd">view</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
+                               <form authority="smd">aperture card</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
+                               <form authority="smd">microfiche</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
+                               <form authority="smd">microfiche cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
+                               <form authority="smd">microfilm cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
+                               <form authority="smd">microfilm cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
+                               <form authority="smd">microfilm reel</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
+                               <form authority="smd">microopaque</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
+                               <form authority="smd">film cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
+                               <form authority="smd">film cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
+                               <form authority="smd">film reel</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
+                               <form authority="smd">chart</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
+                               <form authority="smd">collage</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
+                               <form authority="smd">drawing</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
+                               <form authority="smd">flash card</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
+                               <form authority="smd">painting</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
+                               <form authority="smd">photomechanical print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
+                               <form authority="smd">photonegative</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
+                               <form authority="smd">photoprint</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
+                               <form authority="smd">picture</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
+                               <form authority="smd">print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
+                               <form authority="smd">technical drawing</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
+                               <form authority="smd">notated music</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
+                               <form authority="smd">filmslip</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
+                               <form authority="smd">filmstrip cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
+                               <form authority="smd">filmstrip roll</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
+                               <form authority="smd">other filmstrip type</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
+                               <form authority="smd">slide</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
+                               <form authority="smd">transparency</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote-sensing image</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
+                               <form authority="smd">cylinder</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
+                               <form authority="smd">roll</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
+                               <form authority="smd">sound cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
+                               <form authority="smd">sound cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
+                               <form authority="smd">sound disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
+                               <form authority="smd">sound-tape reel</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
+                               <form authority="smd">sound-track film</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
+                               <form authority="smd">wire recording</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
+                               <form authority="smd">braille</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
+                               <form authority="smd">combination</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
+                               <form authority="smd">moon</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
+                               <form authority="smd">tactile, with no writing system</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
+                               <form authority="smd">braille</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
+                               <form authority="smd">large print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
+                               <form authority="smd">regular print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
+                               <form authority="smd">text in looseleaf binder</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
+                               <form authority="smd">videocartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
+                               <form authority="smd">videocassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
+                               <form authority="smd">videodisc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
+                               <form authority="smd">videoreel</form>
+                       </xsl:if>
+                       
+                       <xsl:for-each select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)>1]">
+                               <internetMediaType>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </internetMediaType>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=300]">
+                               <extent>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abce</xsl:with-param>
+                                       </xsl:call-template>
+                               </extent>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($physicalDescription))">
+                       <physicalDescription>
+                               <xsl:copy-of select="$physicalDescription"></xsl:copy-of>
+                       </physicalDescription>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=520]">
+                       <abstract>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </abstract>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=505]">
+                       <tableOfContents>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">agrt</xsl:with-param>
+                               </xsl:call-template>
+                       </tableOfContents>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=521]">
+                       <targetAudience>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </targetAudience>
+               </xsl:for-each>
+               <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
+                       <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"></xsl:variable>
+                       <xsl:choose>
+                               <!-- 01/04 fix -->
+                               <xsl:when test="$controlField008-22='d'">
+                                       <targetAudience authority="marctarget">adolescent</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='e'">
+                                       <targetAudience authority="marctarget">adult</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='g'">
+                                       <targetAudience authority="marctarget">general</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
+                                       <targetAudience authority="marctarget">juvenile</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='a'">
+                                       <targetAudience authority="marctarget">preschool</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='f'">
+                                       <targetAudience authority="marctarget">specialized</targetAudience>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
+                       <note type="statement of responsibility">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=500]">
+                       <note>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                       </note>
+               </xsl:for-each>
+               
+               <!--3.2 change tmee additional note fields-->
+               
+               <xsl:for-each select="marc:datafield[@tag=506]">
+                       <note type="restrictions">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=510]">
+                       <note  type="citation/reference">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+                       
+               <xsl:for-each select="marc:datafield[@tag=511]">
+                       <note type="performers">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=518]">
+                       <note type="venue">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=530]">
+                       <note  type="additional physical form">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=533]">
+                       <note  type="reproduction">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=534]">
+                       <note  type="original version">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=538]">
+                       <note  type="system details">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=583]">
+                       <note type="action">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+
+               
+               
+               
+               <xsl:for-each select="marc:datafield[@tag=501 or @tag=502 or @tag=504 or @tag=507 or @tag=508 or  @tag=513 or @tag=514 or @tag=515 or @tag=516 or @tag=522 or @tag=524 or @tag=525 or @tag=526 or @tag=535 or @tag=536 or @tag=540 or @tag=541 or @tag=544 or @tag=545 or @tag=546 or @tag=547 or @tag=550 or @tag=552 or @tag=555 or @tag=556 or @tag=561 or @tag=562 or @tag=565 or @tag=567 or @tag=580 or @tag=581 or @tag=584 or @tag=585 or @tag=586]">
+                       <note>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
+                       <subject>
+                               <cartographics>
+                                       <coordinates>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">defg</xsl:with-param>
+                                               </xsl:call-template>
+                                       </coordinates>
+                               </cartographics>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=043]">
+                       <subject>
+                               <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
+                                       <geographicCode>
+                                               <xsl:attribute name="authority">
+                                                       <xsl:if test="@code='a'">
+                                                               <xsl:text>marcgac</xsl:text>
+                                                       </xsl:if>
+                                                       <xsl:if test="@code='b'">
+                                                               <xsl:value-of select="following-sibling::marc:subfield[@code=2]"></xsl:value-of>
+                                                       </xsl:if>
+                                                       <xsl:if test="@code='c'">
+                                                               <xsl:text>iso3166</xsl:text>
+                                                       </xsl:if>
+                                               </xsl:attribute>
+                                               <xsl:value-of select="self::marc:subfield"></xsl:value-of>
+                                       </geographicCode>
+                               </xsl:for-each>
+                       </subject>
+               </xsl:for-each>
+               <!-- tmee 2006/11/27 -->
+               <xsl:for-each select="marc:datafield[@tag=255]">
+                       <subject>
+                               <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
+                               <cartographics>
+                                       <xsl:if test="@code='a'">
+                                               <scale>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </scale>
+                                       </xsl:if>
+                                       <xsl:if test="@code='b'">
+                                               <projection>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </projection>
+                                       </xsl:if>
+                                       <xsl:if test="@code='c'">
+                                               <coordinates>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </coordinates>
+                                       </xsl:if>
+                               </cartographics>
+                               </xsl:for-each>
+                       </subject>
+               </xsl:for-each>
+                               
+               <xsl:apply-templates select="marc:datafield[653 >= @tag and @tag >= 600]"></xsl:apply-templates>
+               <xsl:apply-templates select="marc:datafield[@tag=656]"></xsl:apply-templates>
+               <xsl:for-each select="marc:datafield[@tag=752]">
+                       <subject>
+                               <hierarchicalGeographic>
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <country>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </country>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <state>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </state>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='c']">
+                                               <county>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </county>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='d']">
+                                               <city>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </city>
+                                       </xsl:for-each>
+                               </hierarchicalGeographic>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
+                       <subject>
+                               <xsl:choose>
+                                       <xsl:when test="@ind1=2">
+                                               <temporal encoding="iso8601" point="start">
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString">
+                                                                       <xsl:value-of select="marc:subfield[@code='b'][1]"></xsl:value-of>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </temporal>
+                                               <temporal encoding="iso8601" point="end">
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString">
+                                                                       <xsl:value-of select="marc:subfield[@code='b'][2]"></xsl:value-of>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </temporal>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:for-each select="marc:subfield[@code='b']">
+                                                       <temporal encoding="iso8601">
+                                                               <xsl:call-template name="chopPunctuation">
+                                                                       <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </temporal>
+                                               </xsl:for-each>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=050]">
+                       <xsl:for-each select="marc:subfield[@code='b']">
+                               <classification authority="lcc">
+                                       <xsl:if test="../marc:subfield[@code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"></xsl:value-of>
+                                       <xsl:text> </xsl:text>
+                                       <xsl:value-of select="text()"></xsl:value-of>
+                               </classification>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
+                               <classification authority="lcc">
+                                       <xsl:if test="../marc:subfield[@code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="text()"></xsl:value-of>
+                               </classification>
+                       </xsl:for-each>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=082]">
+                       <classification authority="ddc">
+                               <xsl:if test="marc:subfield[@code='2']">
+                                       <xsl:attribute name="edition">
+                                               <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:if>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=080]">
+                       <classification authority="udc">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abx</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=060]">
+                       <classification authority="nlm">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
+                       <classification authority="sudocs">
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
+                       <classification authority="candoc">
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086]">
+                       <classification>
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                               </xsl:attribute>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=084]">
+                       <classification>
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                               </xsl:attribute>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=440]">
+                       <relatedItem type="series">
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">av</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:value-of select="$titleChop" />
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <titleInfo type="nfi">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind2>0">
+                                                       <nonSort>
+                                                               <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                                       </nonSort>
+                                                       <title>
+                                                               <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                                       </title>
+                                                       <xsl:call-template name="part"/>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <title>
+                                                               <xsl:value-of select="$titleChop" />
+                                                       </title>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">av</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=510]">
+                       <relatedItem type="isReferencedBy">
+                               <note>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abcx3</xsl:with-param>
+                                       </xsl:call-template>
+                               </note>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=534]">
+                       <relatedItem type="original">
+                               <xsl:call-template name="relatedTitle"></xsl:call-template>
+                               <xsl:call-template name="relatedName"></xsl:call-template>
+                               <xsl:if test="marc:subfield[@code='b' or @code='c']">
+                                       <originInfo>
+                                               <xsl:for-each select="marc:subfield[@code='c']">
+                                                       <publisher>
+                                                               <xsl:value-of select="."></xsl:value-of>
+                                                       </publisher>
+                                               </xsl:for-each>
+                                               <xsl:for-each select="marc:subfield[@code='b']">
+                                                       <edition>
+                                                               <xsl:value-of select="."></xsl:value-of>
+                                                       </edition>
+                                               </xsl:for-each>
+                                       </originInfo>
+                               </xsl:if>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                               <xsl:for-each select="marc:subfield[@code='z']">
+                                       <identifier type="isbn">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </identifier>
+                               </xsl:for-each>
+                               <xsl:call-template name="relatedNote"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <name type="personal">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aq</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">g</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="termsOfAddress"></xsl:call-template>
+                                       <xsl:call-template name="nameDate"></xsl:call-template>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">dg</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </titleInfo>
+                               <name type="corporate">
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:variable name="tempNamePart">
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">c</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:variable>
+                                       <xsl:if test="normalize-space($tempNamePart)">
+                                               <namePart>
+                                                       <xsl:value-of select="$tempNamePart"></xsl:value-of>
+                                               </namePart>
+                                       </xsl:if>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </titleInfo>
+                               <name type="conference">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">gn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:value-of select="$titleChop" />
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <titleInfo type="nfi">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind1>0">
+                                                       <nonSort>
+                                                               <xsl:value-of select="substring($titleChop,1,@ind1)"/>
+                                                       </nonSort>
+                                                       <title>
+                                                               <xsl:value-of select="substring($titleChop,@ind1+1)"/>
+                                                       </title>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <title>
+                                                               <xsl:value-of select="$titleChop" />
+                                                       </title>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
+                       <relatedItem type="series">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
+                       <relatedItem>
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=775]">
+                       <relatedItem type="otherVersion">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
+                       <relatedItem type="constituent">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
+                       <relatedItem type="host">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=776]">
+                       <relatedItem type="otherFormat">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=780]">
+                       <relatedItem type="preceding">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=785]">
+                       <relatedItem type="succeeding">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=786]">
+                       <relatedItem type="original">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=800]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <name type="personal">
+                                       <namePart>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">aq</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="beforeCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="termsOfAddress"></xsl:call-template>
+                                       <xsl:call-template name="nameDate"></xsl:call-template>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=810]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">dg</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </titleInfo>
+                               <name type="corporate">
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">c</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=811]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </titleInfo>
+                               <name type="conference">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">gn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="role"/>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='830']">
+                       <relatedItem type="series">
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:value-of select="$titleChop" />
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <titleInfo type="nfi">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind2>0">
+                                                       <nonSort>
+                                                               <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                                       </nonSort>
+                                                       <title>
+                                                               <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                                       </title>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <title>
+                                                               <xsl:value-of select="$titleChop" />
+                                                       </title>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
+                       <relatedItem>
+                               <internetMediaType>
+                                       <xsl:value-of select="."/>
+                               </internetMediaType>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='020']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">isbn</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="isbn">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">isrc</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="isrc">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">ismn</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="ismn">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">sici</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="sici">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='022']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">issn</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="issn">
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='010']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">lccn</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="lccn">
+                               <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='028']">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind1='0'">issue number</xsl:when>
+                                               <xsl:when test="@ind1='1'">matrix number</xsl:when>
+                                               <xsl:when test="@ind1='2'">music plate</xsl:when>
+                                               <xsl:when test="@ind1='3'">music publisher</xsl:when>
+                                               <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
+                                       </xsl:choose>
+                               </xsl:attribute>
+                               <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 028 -->
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">
+                                               <xsl:choose>
+                                                       <xsl:when test="@ind1='0'">ba</xsl:when>
+                                                       <xsl:otherwise>ab</xsl:otherwise>
+                                               </xsl:choose>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='037']">
+                       <identifier type="stock number">
+                               <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 037 -->
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:choose>
+                                               <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')">doi</xsl:when>
+                                               <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov')">hdl</xsl:when>
+                                               <xsl:otherwise>uri</xsl:otherwise>
+                                       </xsl:choose>
+                               </xsl:attribute>
+                               <xsl:choose>
+                                       <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov') ">
+                                               <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </identifier>
+                       <xsl:if test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
+                               <identifier type="hdl">
+                                       <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">y3z</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
+                       <identifier type="upc">
+                               <xsl:call-template name="isInvalid"/>
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </identifier>
+               </xsl:for-each>
+               <!-- 1/04 fix added $y -->
+               <xsl:for-each select="marc:datafield[@tag=856][marc:subfield[@code='u']]">
+                       <location>
+                               <url>
+                                       <xsl:if test="marc:subfield[@code='y' or @code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">y3</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:if test="marc:subfield[@code='z' ]">
+                                               <xsl:attribute name="note">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">z</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
+
+                               </url>
+                       </location>
+               </xsl:for-each>
+                       
+                       <!-- 3.2 change tmee 856z  -->
+
+               
+               <xsl:for-each select="marc:datafield[@tag=852]">
+                       <location>
+                               <physicalLocation>
+                                       <xsl:call-template name="displayLabel"></xsl:call-template>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abje</xsl:with-param>
+                                       </xsl:call-template>
+                               </physicalLocation>
+                       </location>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=506]">
+                       <accessCondition type="restrictionOnAccess">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abcd35</xsl:with-param>
+                               </xsl:call-template>
+                       </accessCondition>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=540]">
+                       <accessCondition type="useAndReproduction">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abcde35</xsl:with-param>
+                               </xsl:call-template>
+                       </accessCondition>
+               </xsl:for-each>
+               <recordInfo>
+                       <xsl:for-each select="marc:datafield[@tag=040]">
+                               <recordContentSource authority="marcorg">
+                                       <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                               </recordContentSource>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=008]">
+                               <recordCreationDate encoding="marc">
+                                       <xsl:value-of select="substring(.,1,6)"></xsl:value-of>
+                               </recordCreationDate>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=005]">
+                               <recordChangeDate encoding="iso8601">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </recordChangeDate>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=001]">
+                               <recordIdentifier>
+                                       <xsl:if test="../marc:controlfield[@tag=003]">
+                                               <xsl:attribute name="source">
+                                                       <xsl:value-of select="../marc:controlfield[@tag=003]"></xsl:value-of>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </recordIdentifier>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
+                               <languageOfCataloging>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </languageTerm>
+                               </languageOfCataloging>
+                       </xsl:for-each>
+               </recordInfo>
+       </xsl:template>
+       <xsl:template name="displayForm">
+               <xsl:for-each select="marc:subfield[@code='c']">
+                       <displayForm>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </displayForm>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="affiliation">
+               <xsl:for-each select="marc:subfield[@code='u']">
+                       <affiliation>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </affiliation>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="uri">
+               <xsl:for-each select="marc:subfield[@code='u']">
+                       <xsl:attribute name="xlink:href">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </xsl:attribute>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='0']">
+                       <xsl:choose>
+                               <xsl:when test="contains(text(), ')')">
+                                       <xsl:attribute name="xlink:href">
+                                               <xsl:value-of select="substring-after(text(), ')')"></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:attribute name="xlink:href">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="role">
+               <xsl:for-each select="marc:subfield[@code='e']">
+                       <role>
+                               <roleTerm type="text">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </roleTerm>
+                       </role>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='4']">
+                       <role>
+                               <roleTerm authority="marcrelator" type="code">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </roleTerm>
+                       </role>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="part">
+               <xsl:variable name="partNumber">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">n</xsl:with-param>
+                               <xsl:with-param name="anyCodes">n</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:variable name="partName">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">p</xsl:with-param>
+                               <xsl:with-param name="anyCodes">p</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($partNumber))">
+                       <partNumber>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="$partNumber"></xsl:with-param>
+                               </xsl:call-template>
+                       </partNumber>
+               </xsl:if>
+               <xsl:if test="string-length(normalize-space($partName))">
+                       <partName>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="$partName"></xsl:with-param>
+                               </xsl:call-template>
+                       </partName>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedPart">
+               <xsl:if test="@tag=773">
+                       <xsl:for-each select="marc:subfield[@code='g']">
+                               <part>
+                                       <text>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </text>
+                               </part>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:subfield[@code='q']">
+                               <part>
+                                       <xsl:call-template name="parsePart"></xsl:call-template>
+                               </part>
+                       </xsl:for-each>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedPartNumName">
+               <xsl:variable name="partNumber">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">g</xsl:with-param>
+                               <xsl:with-param name="anyCodes">g</xsl:with-param>
+                               <xsl:with-param name="afterCodes">pst</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:variable name="partName">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">p</xsl:with-param>
+                               <xsl:with-param name="anyCodes">p</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($partNumber))">
+                       <partNumber>
+                               <xsl:value-of select="$partNumber"></xsl:value-of>
+                       </partNumber>
+               </xsl:if>
+               <xsl:if test="string-length(normalize-space($partName))">
+                       <partName>
+                               <xsl:value-of select="$partName"></xsl:value-of>
+                       </partName>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedName">
+               <xsl:for-each select="marc:subfield[@code='a']">
+                       <name>
+                               <namePart>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </namePart>
+                       </name>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedForm">
+               <xsl:for-each select="marc:subfield[@code='h']">
+                       <physicalDescription>
+                               <form>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </form>
+                       </physicalDescription>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedExtent">
+               <xsl:for-each select="marc:subfield[@code='h']">
+                       <physicalDescription>
+                               <extent>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </extent>
+                       </physicalDescription>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedNote">
+               <xsl:for-each select="marc:subfield[@code='n']">
+                       <note>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedSubject">
+               <xsl:for-each select="marc:subfield[@code='j']">
+                       <subject>
+                               <temporal encoding="iso8601">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                       </xsl:call-template>
+                               </temporal>
+                       </subject>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifierISSN">
+               <xsl:for-each select="marc:subfield[@code='x']">
+                       <identifier type="issn">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifierLocal">
+               <xsl:for-each select="marc:subfield[@code='w']">
+                       <identifier type="local">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifier">
+               <xsl:for-each select="marc:subfield[@code='o']">
+                       <identifier>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedItem76X-78X">
+               <xsl:call-template name="displayLabel"></xsl:call-template>
+               <xsl:call-template name="relatedTitle76X-78X"></xsl:call-template>
+               <xsl:call-template name="relatedName"></xsl:call-template>
+               <xsl:call-template name="relatedOriginInfo"></xsl:call-template>
+               <xsl:call-template name="relatedLanguage"></xsl:call-template>
+               <xsl:call-template name="relatedExtent"></xsl:call-template>
+               <xsl:call-template name="relatedNote"></xsl:call-template>
+               <xsl:call-template name="relatedSubject"></xsl:call-template>
+               <xsl:call-template name="relatedIdentifier"></xsl:call-template>
+               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+               <xsl:call-template name="relatedIdentifierLocal"></xsl:call-template>
+               <xsl:call-template name="relatedPart"></xsl:call-template>
+       </xsl:template>
+       <xsl:template name="subjectGeographicZ">
+               <geographic>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </geographic>
+       </xsl:template>
+       <xsl:template name="subjectTemporalY">
+               <temporal>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </temporal>
+       </xsl:template>
+       <xsl:template name="subjectTopic">
+               <topic>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </topic>
+       </xsl:template> 
+       <!-- 3.2 change tmee 6xx $v genre -->
+       <xsl:template name="subjectGenre">
+               <genre>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </genre>
+       </xsl:template>
+       
+       <xsl:template name="nameABCDN">
+               <xsl:for-each select="marc:subfield[@code='a']">
+                       <namePart>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="."></xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='b']">
+                       <namePart>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </namePart>
+               </xsl:for-each>
+               <xsl:if test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
+                       <namePart>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">cdn</xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="nameABCDQ">
+               <namePart>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString">
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">aq</xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:with-param>
+                               <xsl:with-param name="punctuation">
+                                       <xsl:text>:,;/ </xsl:text>
+                               </xsl:with-param>
+                       </xsl:call-template>
+               </namePart>
+               <xsl:call-template name="termsOfAddress"></xsl:call-template>
+               <xsl:call-template name="nameDate"></xsl:call-template>
+       </xsl:template>
+       <xsl:template name="nameACDEQ">
+               <namePart>
+                       <xsl:call-template name="subfieldSelect">
+                               <xsl:with-param name="codes">acdeq</xsl:with-param>
+                       </xsl:call-template>
+               </namePart>
+       </xsl:template>
+       <xsl:template name="constituentOrRelatedType">
+               <xsl:if test="@ind2=2">
+                       <xsl:attribute name="type">constituent</xsl:attribute>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedTitle">
+               <xsl:for-each select="marc:subfield[@code='t']">
+                       <titleInfo>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                       </titleInfo>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedTitle76X-78X">
+               <xsl:for-each select="marc:subfield[@code='t']">
+                       <titleInfo>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='p']">
+                       <titleInfo type="abbreviated">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='s']">
+                       <titleInfo type="uniform">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedOriginInfo">
+               <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
+                       <originInfo>
+                               <xsl:if test="@tag=775">
+                                       <xsl:for-each select="marc:subfield[@code='f']">
+                                               <place>
+                                                       <placeTerm>
+                                                               <xsl:attribute name="type">code</xsl:attribute>
+                                                               <xsl:attribute name="authority">marcgac</xsl:attribute>
+                                                               <xsl:value-of select="."></xsl:value-of>
+                                                       </placeTerm>
+                                               </place>
+                                       </xsl:for-each>
+                               </xsl:if>
+                               <xsl:for-each select="marc:subfield[@code='d']">
+                                       <publisher>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </publisher>
+                               </xsl:for-each>
+                               <xsl:for-each select="marc:subfield[@code='b']">
+                                       <edition>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </edition>
+                               </xsl:for-each>
+                       </originInfo>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedLanguage">
+               <xsl:for-each select="marc:subfield[@code='e']">
+                       <xsl:call-template name="getLanguage">
+                               <xsl:with-param name="langString">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </xsl:with-param>
+                       </xsl:call-template>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="nameDate">
+               <xsl:for-each select="marc:subfield[@code='d']">
+                       <namePart type="date">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="."></xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="subjectAuthority">
+               <xsl:if test="@ind2!=4">
+                       <xsl:if test="@ind2!=' '">
+                               <xsl:if test="@ind2!=8">
+                                       <xsl:if test="@ind2!=9">
+                                               <xsl:attribute name="authority">
+                                                       <xsl:choose>
+                                                               <xsl:when test="@ind2=0">lcsh</xsl:when>
+                                                               <xsl:when test="@ind2=1">lcshac</xsl:when>
+                                                               <xsl:when test="@ind2=2">mesh</xsl:when>
+                                                               <!-- 1/04 fix -->
+                                                               <xsl:when test="@ind2=3">nal</xsl:when>
+                                                               <xsl:when test="@ind2=5">csh</xsl:when>
+                                                               <xsl:when test="@ind2=6">rvm</xsl:when>
+                                                               <xsl:when test="@ind2=7">
+                                                                       <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                                                               </xsl:when>
+                                                       </xsl:choose>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                               </xsl:if>
+                       </xsl:if>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="subjectAnyOrder">
+               <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
+                       <xsl:choose>
+                               <xsl:when test="@code='v'">
+                                       <xsl:call-template name="subjectGenre"></xsl:call-template>
+                               </xsl:when>
+                               <xsl:when test="@code='x'">
+                                       <xsl:call-template name="subjectTopic"></xsl:call-template>
+                               </xsl:when>
+                               <xsl:when test="@code='y'">
+                                       <xsl:call-template name="subjectTemporalY"></xsl:call-template>
+                               </xsl:when>
+                               <xsl:when test="@code='z'">
+                                       <xsl:call-template name="subjectGeographicZ"></xsl:call-template>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="specialSubfieldSelect">
+               <xsl:param name="anyCodes"></xsl:param>
+               <xsl:param name="axis"></xsl:param>
+               <xsl:param name="beforeCodes"></xsl:param>
+               <xsl:param name="afterCodes"></xsl:param>
+               <xsl:variable name="str">
+                       <xsl:for-each select="marc:subfield">
+                               <xsl:if test="contains($anyCodes, @code)      or (contains($beforeCodes,@code) and following-sibling::marc:subfield[@code=$axis])      or (contains($afterCodes,@code) and preceding-sibling::marc:subfield[@code=$axis])">
+                                       <xsl:value-of select="text()"></xsl:value-of>
+                                       <xsl:text> </xsl:text>
+                               </xsl:if>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+       </xsl:template>
+       
+       <!-- 3.2 change tmee 6xx $v genre -->
+       <xsl:template match="marc:datafield[@tag=600]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <name type="personal">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="termsOfAddress"></xsl:call-template>
+                               <namePart>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">aq</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </namePart>
+                               <xsl:call-template name="nameDate"></xsl:call-template>
+                               <xsl:call-template name="affiliation"></xsl:call-template>
+                               <xsl:call-template name="role"></xsl:call-template>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=610]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <name type="corporate">
+                               <xsl:call-template name="uri" />
+                               <xsl:for-each select="marc:subfield[@code='a']">
+                                       <namePart>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </namePart>
+                               </xsl:for-each>
+                               <xsl:for-each select="marc:subfield[@code='b']">
+                                       <namePart>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </namePart>
+                               </xsl:for-each>
+                               <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
+                                       <namePart>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">cdnp</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                               </xsl:if>
+                               <xsl:call-template name="role"></xsl:call-template>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=611]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <name type="conference">
+                               <xsl:call-template name="uri" />
+                               <namePart>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
+                                       </xsl:call-template>
+                               </namePart>
+                               <xsl:for-each select="marc:subfield[@code='4']">
+                                       <role>
+                                               <roleTerm authority="marcrelator" type="code">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </roleTerm>
+                                       </role>
+                               </xsl:for-each>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=630]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">adfhklor</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo>
+                               <title>
+                                       <xsl:value-of select="$titleChop" />
+                               </title>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleInfo>
+                       <titleInfo type="nfi">
+                               <xsl:choose>
+                                       <xsl:when test="@ind1>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind1)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind1+1)"/>
+                                               </title>
+                                               <xsl:call-template name="part"/>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop" />
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleInfo>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=650]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <topic>
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">abcd</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </topic>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=651]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <xsl:for-each select="marc:subfield[@code='a']">
+                               <geographic>
+                                       <xsl:call-template name="uri" />
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                       </xsl:call-template>
+                               </geographic>
+                       </xsl:for-each>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=653]">
+               <subject>
+                       <xsl:for-each select="marc:subfield[@code='a']">
+                               <topic>
+                                       <xsl:call-template name="uri" />
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </topic>
+                       </xsl:for-each>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=656]">
+               <subject>
+                       <xsl:if test="marc:subfield[@code=2]">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code=2]"></xsl:value-of>
+                               </xsl:attribute>
+                       </xsl:if>
+                       <occupation>
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </occupation>
+               </subject>
+       </xsl:template>
+       <xsl:template name="termsOfAddress">
+               <xsl:if test="marc:subfield[@code='b' or @code='c']">
+                       <namePart type="termsOfAddress">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">bc</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="displayLabel">
+               <xsl:if test="marc:subfield[@code='i']">
+                       <xsl:attribute name="displayLabel">
+                               <xsl:value-of select="marc:subfield[@code='i']"></xsl:value-of>
+                       </xsl:attribute>
+               </xsl:if>
+               <xsl:if test="marc:subfield[@code='3']">
+                       <xsl:attribute name="displayLabel">
+                               <xsl:value-of select="marc:subfield[@code='3']"></xsl:value-of>
+                       </xsl:attribute>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="isInvalid">
+               <xsl:param name="type"/>
+               <xsl:if test="marc:subfield[@code='z'] or marc:subfield[@code='y']">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:value-of select="$type"/>
+                               </xsl:attribute>
+                               <xsl:attribute name="invalid">
+                                       <xsl:text>yes</xsl:text>
+                               </xsl:attribute>
+                               <xsl:if test="marc:subfield[@code='z']">
+                                       <xsl:value-of select="marc:subfield[@code='z']"/>
+                               </xsl:if>
+                               <xsl:if test="marc:subfield[@code='y']">
+                                       <xsl:value-of select="marc:subfield[@code='y']"/>
+                               </xsl:if>
+                       </identifier>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="subtitle">
+               <xsl:if test="marc:subfield[@code='b']">
+                       <subTitle>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="marc:subfield[@code='b']"/>
+                                               <!--<xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">b</xsl:with-param>                                                                 
+                                               </xsl:call-template>-->
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </subTitle>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="script">
+               <xsl:param name="scriptCode"></xsl:param>
+               <xsl:attribute name="script">
+                       <xsl:choose>
+                               <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
+                               <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
+                               <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
+                               <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
+                               <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
+                               <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
+                       </xsl:choose>
+               </xsl:attribute>
+       </xsl:template>
+       <xsl:template name="parsePart">
+               <!-- assumes 773$q= 1:2:3<4
+                    with up to 3 levels and one optional start page
+               -->
+               <xsl:variable name="level1">
+                       <xsl:choose>
+                               <xsl:when test="contains(text(),':')">
+                                       <!-- 1:2 -->
+                                       <xsl:value-of select="substring-before(text(),':')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:when test="not(contains(text(),':'))">
+                                       <!-- 1 or 1<3 -->
+                                       <xsl:if test="contains(text(),'&lt;')">
+                                               <!-- 1<3 -->
+                                               <xsl:value-of select="substring-before(text(),'&lt;')"></xsl:value-of>
+                                       </xsl:if>
+                                       <xsl:if test="not(contains(text(),'&lt;'))">
+                                               <!-- 1 -->
+                                               <xsl:value-of select="text()"></xsl:value-of>
+                                       </xsl:if>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="sici2">
+                       <xsl:choose>
+                               <xsl:when test="starts-with(substring-after(text(),$level1),':')">
+                                       <xsl:value-of select="substring(substring-after(text(),$level1),2)"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="substring-after(text(),$level1)"></xsl:value-of>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="level2">
+                       <xsl:choose>
+                               <xsl:when test="contains($sici2,':')">
+                                       <!--  2:3<4  -->
+                                       <xsl:value-of select="substring-before($sici2,':')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:when test="contains($sici2,'&lt;')">
+                                       <!-- 1: 2<4 -->
+                                       <xsl:value-of select="substring-before($sici2,'&lt;')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="$sici2"></xsl:value-of>
+                                       <!-- 1:2 -->
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="sici3">
+                       <xsl:choose>
+                               <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
+                                       <xsl:value-of select="substring(substring-after($sici2,$level2),2)"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="substring-after($sici2,$level2)"></xsl:value-of>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="level3">
+                       <xsl:choose>
+                               <xsl:when test="contains($sici3,'&lt;')">
+                                       <!-- 2<4 -->
+                                       <xsl:value-of select="substring-before($sici3,'&lt;')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="$sici3"></xsl:value-of>
+                                       <!-- 3 -->
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="page">
+                       <xsl:if test="contains(text(),'&lt;')">
+                               <xsl:value-of select="substring-after(text(),'&lt;')"></xsl:value-of>
+                       </xsl:if>
+               </xsl:variable>
+               <xsl:if test="$level1">
+                       <detail level="1">
+                               <number>
+                                       <xsl:value-of select="$level1"></xsl:value-of>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$level2">
+                       <detail level="2">
+                               <number>
+                                       <xsl:value-of select="$level2"></xsl:value-of>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$level3">
+                       <detail level="3">
+                               <number>
+                                       <xsl:value-of select="$level3"></xsl:value-of>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$page">
+                       <extent unit="page">
+                               <start>
+                                       <xsl:value-of select="$page"></xsl:value-of>
+                               </start>
+                       </extent>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="getLanguage">
+               <xsl:param name="langString"></xsl:param>
+               <xsl:param name="controlField008-35-37"></xsl:param>
+               <xsl:variable name="length" select="string-length($langString)"></xsl:variable>
+               <xsl:choose>
+                       <xsl:when test="$length=0"></xsl:when>
+                       <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
+                               <xsl:call-template name="getLanguage">
+                                       <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
+                                       <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <language>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="substring($langString,1,3)"></xsl:value-of>
+                                       </languageTerm>
+                               </language>
+                               <xsl:call-template name="getLanguage">
+                                       <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
+                                       <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="isoLanguage">
+               <xsl:param name="currentLanguage"></xsl:param>
+               <xsl:param name="usedLanguages"></xsl:param>
+               <xsl:param name="remainingLanguages"></xsl:param>
+               <xsl:choose>
+                       <xsl:when test="string-length($currentLanguage)=0"></xsl:when>
+                       <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
+                               <language>
+                                       <xsl:if test="@code!='a'">
+                                               <xsl:attribute name="objectPart">
+                                                       <xsl:choose>
+                                                               <xsl:when test="@code='b'">summary or subtitle</xsl:when>
+                                                               <xsl:when test="@code='d'">sung or spoken text</xsl:when>
+                                                               <xsl:when test="@code='e'">libretto</xsl:when>
+                                                               <xsl:when test="@code='f'">table of contents</xsl:when>
+                                                               <xsl:when test="@code='g'">accompanying material</xsl:when>
+                                                               <xsl:when test="@code='h'">translation</xsl:when>
+                                                       </xsl:choose>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="$currentLanguage"></xsl:value-of>
+                                       </languageTerm>
+                               </language>
+                               <xsl:call-template name="isoLanguage">
+                                       <xsl:with-param name="currentLanguage">
+                                               <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="usedLanguages">
+                                               <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="remainingLanguages">
+                                               <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <xsl:call-template name="isoLanguage">
+                                       <xsl:with-param name="currentLanguage">
+                                               <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="usedLanguages">
+                                               <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="remainingLanguages">
+                                               <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="chopBrackets">
+               <xsl:param name="chopString"></xsl:param>
+               <xsl:variable name="string">
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="$chopString"></xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="substring($string, 1,1)='['">
+                       <xsl:value-of select="substring($string,2, string-length($string)-2)"></xsl:value-of>
+               </xsl:if>
+               <xsl:if test="substring($string, 1,1)!='['">
+                       <xsl:value-of select="$string"></xsl:value-of>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="rfcLanguages">
+               <xsl:param name="nodeNum"></xsl:param>
+               <xsl:param name="usedLanguages"></xsl:param>
+               <xsl:param name="controlField008-35-37"></xsl:param>
+               <xsl:variable name="currentLanguage" select="."></xsl:variable>
+               <xsl:choose>
+                       <xsl:when test="not($currentLanguage)"></xsl:when>
+                       <xsl:when test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
+                               <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
+                                       <language>
+                                               <xsl:if test="@code!='a'">
+                                                       <xsl:attribute name="objectPart">
+                                                               <xsl:choose>
+                                                                       <xsl:when test="@code='b'">summary or subtitle</xsl:when>
+                                                                       <xsl:when test="@code='d'">sung or spoken text</xsl:when>
+                                                                       <xsl:when test="@code='e'">libretto</xsl:when>
+                                                                       <xsl:when test="@code='f'">table of contents</xsl:when>
+                                                                       <xsl:when test="@code='g'">accompanying material</xsl:when>
+                                                                       <xsl:when test="@code='h'">translation</xsl:when>
+                                                               </xsl:choose>
+                                                       </xsl:attribute>
+                                               </xsl:if>
+                                               <languageTerm authority="rfc3066" type="code">
+                                                       <xsl:value-of select="$currentLanguage"/>
+                                               </languageTerm>
+                                       </language>
+                               </xsl:if>
+                       </xsl:when>
+                       <xsl:otherwise>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="datafield">
+               <xsl:param name="tag"/>
+               <xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
+               <xsl:param name="ind2"><xsl:text> </xsl:text></xsl:param>
+               <xsl:param name="subfields"/>
+               <xsl:element name="marc:datafield">
+                       <xsl:attribute name="tag">
+                               <xsl:value-of select="$tag"/>
+                       </xsl:attribute>
+                       <xsl:attribute name="ind1">
+                               <xsl:value-of select="$ind1"/>
+                       </xsl:attribute>
+                       <xsl:attribute name="ind2">
+                               <xsl:value-of select="$ind2"/>
+                       </xsl:attribute>
+                       <xsl:copy-of select="$subfields"/>
+               </xsl:element>
+       </xsl:template>
+
+       <xsl:template name="subfieldSelect">
+               <xsl:param name="codes"/>
+               <xsl:param name="delimeter"><xsl:text> </xsl:text></xsl:param>
+               <xsl:variable name="str">
+                       <xsl:for-each select="marc:subfield">
+                               <xsl:if test="contains($codes, @code)">
+                                       <xsl:value-of select="text()"/><xsl:value-of select="$delimeter"/>
+                               </xsl:if>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
+       </xsl:template>
+
+       <xsl:template name="buildSpaces">
+               <xsl:param name="spaces"/>
+               <xsl:param name="char"><xsl:text> </xsl:text></xsl:param>
+               <xsl:if test="$spaces>0">
+                       <xsl:value-of select="$char"/>
+                       <xsl:call-template name="buildSpaces">
+                               <xsl:with-param name="spaces" select="$spaces - 1"/>
+                               <xsl:with-param name="char" select="$char"/>
+                       </xsl:call-template>
+               </xsl:if>
+       </xsl:template>
+
+       <xsl:template name="chopPunctuation">
+               <xsl:param name="chopString"/>
+               <xsl:param name="punctuation"><xsl:text>.:,;/ </xsl:text></xsl:param>
+               <xsl:variable name="length" select="string-length($chopString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
+                                       <xsl:with-param name="punctuation" select="$punctuation"/>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:when test="not($chopString)"/>
+                       <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+
+       <xsl:template name="chopPunctuationFront">
+               <xsl:param name="chopString"/>
+               <xsl:variable name="length" select="string-length($chopString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
+                               <xsl:call-template name="chopPunctuationFront">
+                                       <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"/>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:when test="not($chopString)"/>
+                       <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+</xsl:stylesheet>$$ WHERE name = 'mods32';
+
+
+-- 954.data.MODS33-xsl.sql
+UPDATE config.xml_transform SET xslt=$$<xsl:stylesheet xmlns="http://www.loc.gov/mods/v3" xmlns:marc="http://www.loc.gov/MARC21/slim"
+       xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+       exclude-result-prefixes="xlink marc" version="1.0">
+       <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
+
+       <xsl:variable name="ascii">
+               <xsl:text> !"#$%&amp;'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~</xsl:text>
+       </xsl:variable>
+
+       <xsl:variable name="latin1">
+               <xsl:text> ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ</xsl:text>
+       </xsl:variable>
+       <!-- Characters that usually don't need to be escaped -->
+       <xsl:variable name="safe">
+               <xsl:text>!'()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~</xsl:text>
+       </xsl:variable>
+
+       <xsl:variable name="hex">0123456789ABCDEF</xsl:variable>
+
+    <!-- Evergreen specific: revert Revision 1.23, so we can have those authority xlink attributes back. -->
+
+       <!--MARC21slim2MODS3-3.xsl
+Revision 1.27 - Mapped 648 to <subject> 2009/03/13 tmee
+Revision 1.26 - Added subfield $s mapping for 130/240/730  2008/10/16 tmee
+Revision 1.25 - Mapped 040e to <descriptiveStandard> and Leader/18 to <descriptive standard>aacr2  2008/09/18 tmee
+Revision 1.24 - Mapped 852 subfields $h, $i, $j, $k, $l, $m, $t to <shelfLocation> and 852 subfield $u to <physicalLocation> with @xlink 2008/09/17 tmee
+Revision 1.23 - Commented out xlink/uri for subfield 0 for 130/240/730, 100/700, 110/710, 111/711 as these are currently unactionable  2008/09/17  tmee
+Revision 1.22 - Mapped 022 subfield $l to type "issn-l" subfield $m to output identifier element with corresponding @type and @invalid eq 'yes'2008/09/17  tmee
+Revision 1.21 - Mapped 856 ind2=1 or ind2=2 to <relatedItem><location><url>  2008/07/03  tmee
+Revision 1.20 - Added genre w/@auth="contents of 2" and type= "musical composition"  2008/07/01  tmee
+Revision 1.19 - Added genre offprint for 008/24+ BK code 2  2008/07/01  tmee
+Revision 1.18 - Added xlink/uri for subfield 0 for 130/240/730, 100/700, 110/710, 111/711  2008/06/26  tmee
+Revision 1.17 - Added mapping of 662 2008/05/14 tmee   
+Revision 1.16 - Changed @authority from "marc" to "marcgt" for 007 and 008 codes mapped to a term in <genre> 2007/07/10  tmee
+Revision 1.15 - For field 630, moved call to part template outside title element  2007/07/10  tmee
+Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements with corresponding @type and @invalid eq 'yes' when subfields z or y (in the case of 022) exist in the MARCXML ::: 2007/01/04 17:35:20 cred
+Revision 1.13 - Changed order of output under cartographics to reflect schema  2006/11/28  tmee
+Revision 1.12 - Updated to reflect MODS 3.2 Mapping  2006/10/11  tmee
+Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>  2006/04/08  jrad
+Revision 1.10 - MODS 3.1 revisions to language and classification elements  (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)  2006/02/06  ggar
+Revision 1.9 - Subfield $y was added to field 242 2004/09/02 10:57 jrad
+Revision 1.8 - Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
+Revision 1.7 - 2004/03/25 08:29 jrad
+Revision 1.6 - Various validation fixes 2004/02/20 ntra
+Revision 1.5 - MODS2 to MODS3 updates, language unstacking and de-duping, chopPunctuation expanded  2003/10/02 16:18:58  ntra
+Revision 1.3 - Additional Changes not related to MODS Version 2.0 by ntra
+Revision 1.2 - Added Log Comment  2003/03/24 19:37:42  ckeith
+-->
+       <xsl:template match="/">
+               <xsl:choose>
+                       <xsl:when test="//marc:collection">
+                               <modsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                                       xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
+                                       <xsl:for-each select="//marc:collection/marc:record">
+                                               <mods version="3.3">
+                                                       <xsl:call-template name="marcRecord"/>
+                                               </mods>
+                                       </xsl:for-each>
+                               </modsCollection>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.3"
+                                       xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
+                                       <xsl:for-each select="//marc:record">
+                                               <xsl:call-template name="marcRecord"/>
+                                       </xsl:for-each>
+                               </mods>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="marcRecord">
+               <xsl:variable name="leader" select="marc:leader"/>
+               <xsl:variable name="leader6" select="substring($leader,7,1)"/>
+               <xsl:variable name="leader7" select="substring($leader,8,1)"/>
+               <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
+               <xsl:variable name="typeOf008">
+                       <xsl:choose>
+                               <xsl:when test="$leader6='a'">
+                                       <xsl:choose>
+                                               <xsl:when
+                                                       test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
+                                               <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
+                                       </xsl:choose>
+                               </xsl:when>
+                               <xsl:when test="$leader6='t'">BK</xsl:when>
+                               <xsl:when test="$leader6='p'">MM</xsl:when>
+                               <xsl:when test="$leader6='m'">CF</xsl:when>
+                               <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
+                               <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
+                               <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'"
+                               >MU</xsl:when>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:for-each select="marc:datafield[@tag='245']">
+                       <titleInfo>
+                               <xsl:variable name="title">
+                                       <xsl:choose>
+                                               <xsl:when test="marc:subfield[@code='b']">
+                                                       <xsl:call-template name="specialSubfieldSelect">
+                                                               <xsl:with-param name="axis">b</xsl:with-param>
+                                                               <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">abfgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                               </xsl:variable>
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="$title"/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <xsl:choose>
+                                       <xsl:when test="@ind2&gt;0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop"/>
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:if test="marc:subfield[@code='b']">
+                                       <subTitle>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="axis">b</xsl:with-param>
+                                                                       <xsl:with-param name="anyCodes">b</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">afgk</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </subTitle>
+                               </xsl:if>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='210']">
+                       <titleInfo type="abbreviated">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">a</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="subtitle"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='242']">
+                       <titleInfo type="translated">
+                               <!--09/01/04 Added subfield $y-->
+                               <xsl:for-each select="marc:subfield[@code='y']">
+                                       <xsl:attribute name="lang">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <xsl:for-each select="marc:subfield[@code='i']">
+                                       <xsl:attribute name="displayLabel">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <!-- 1/04 removed $h, b -->
+                                                               <xsl:with-param name="codes">a</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <!-- 1/04 fix -->
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='246']">
+                       <titleInfo type="alternative">
+                               <xsl:for-each select="marc:subfield[@code='i']">
+                                       <xsl:attribute name="displayLabel">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <!-- 1/04 removed $h, $b -->
+                                                               <xsl:with-param name="codes">af</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each
+                       select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
+                       <titleInfo type="uniform">
+                               <title>
+                                               <xsl:call-template name="uri"/>
+
+                                       <xsl:variable name="str">
+                                               <xsl:for-each select="marc:subfield">
+                                                       <xsl:if
+                                                               test="(contains('adfklmors',@code) and (not(../marc:subfield[@code='n' or @code='p']) or (following-sibling::marc:subfield[@code='n' or @code='p'])))">
+                                                               <xsl:value-of select="text()"/>
+                                                               <xsl:text> </xsl:text>
+                                                       </xsl:if>
+                                               </xsl:for-each>
+                                       </xsl:variable>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
+                       <titleInfo type="alternative">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">ah</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='100']">
+                       <name type="personal">
+
+                               <xsl:call-template name="uri"/>
+
+                               <xsl:call-template name="nameABCDQ"/>
+                               <xsl:call-template name="affiliation"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='110']">
+                       <name type="corporate">
+
+                                       <xsl:call-template name="uri"/>
+
+                               <xsl:call-template name="nameABCDN"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='111']">
+                       <name type="conference">
+
+                                       <xsl:call-template name="uri"/>
+
+                               <xsl:call-template name="nameACDEQ"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
+                       <name type="personal">
+
+                                       <xsl:call-template name="uri"/>
+
+                               <xsl:call-template name="nameABCDQ"/>
+                               <xsl:call-template name="affiliation"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
+                       <name type="corporate">
+
+                                       <xsl:call-template name="uri"/>
+
+                               <xsl:call-template name="nameABCDN"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
+                       <name type="conference">
+
+                                       <xsl:call-template name="uri"/>
+
+                               <xsl:call-template name="nameACDEQ"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
+                       <name>
+                               <xsl:if test="@ind1=1">
+                                       <xsl:attribute name="type">
+                                               <xsl:text>personal</xsl:text>
+                                       </xsl:attribute>
+                               </xsl:if>
+                               <namePart>
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </namePart>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <typeOfResource>
+                       <xsl:if test="$leader7='c'">
+                               <xsl:attribute name="collection">yes</xsl:attribute>
+                       </xsl:if>
+                       <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
+                               <xsl:attribute name="manuscript">yes</xsl:attribute>
+                       </xsl:if>
+                       <xsl:choose>
+                               <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
+                               <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
+                               <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
+                               <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
+                               <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
+                               <xsl:when test="$leader6='k'">still image</xsl:when>
+                               <xsl:when test="$leader6='g'">moving image</xsl:when>
+                               <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
+                               <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
+                               <xsl:when test="$leader6='p'">mixed material</xsl:when>
+                       </xsl:choose>
+               </typeOfResource>
+               <xsl:if test="substring($controlField008,26,1)='d'">
+                       <genre authority="marcgt">globe</genre>
+               </xsl:if>
+               <xsl:if
+                       test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
+                       <genre authority="marcgt">remote-sensing image</genre>
+               </xsl:if>
+               <xsl:if test="$typeOf008='MP'">
+                       <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"/>
+                       <xsl:choose>
+                               <xsl:when
+                                       test="$controlField008-25='a' or $controlField008-25='b' or $controlField008-25='c' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
+                                       <genre authority="marcgt">map</genre>
+                               </xsl:when>
+                               <xsl:when
+                                       test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
+                                       <genre authority="marcgt">atlas</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='SE'">
+                       <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"/>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-21='d'">
+                                       <genre authority="marcgt">database</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='l'">
+                                       <genre authority="marcgt">loose-leaf</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='m'">
+                                       <genre authority="marcgt">series</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='n'">
+                                       <genre authority="marcgt">newspaper</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='p'">
+                                       <genre authority="marcgt">periodical</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='w'">
+                                       <genre authority="marcgt">web site</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
+                       <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"/>
+                       <xsl:choose>
+                               <xsl:when test="contains($controlField008-24,'a')">
+                                       <genre authority="marcgt">abstract or summary</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'b')">
+                                       <genre authority="marcgt">bibliography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'c')">
+                                       <genre authority="marcgt">catalog</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'d')">
+                                       <genre authority="marcgt">dictionary</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'e')">
+                                       <genre authority="marcgt">encyclopedia</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'f')">
+                                       <genre authority="marcgt">handbook</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'g')">
+                                       <genre authority="marcgt">legal article</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'i')">
+                                       <genre authority="marcgt">index</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'k')">
+                                       <genre authority="marcgt">discography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'l')">
+                                       <genre authority="marcgt">legislation</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'m')">
+                                       <genre authority="marcgt">theses</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'n')">
+                                       <genre authority="marcgt">survey of literature</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'o')">
+                                       <genre authority="marcgt">review</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'p')">
+                                       <genre authority="marcgt">programmed text</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'q')">
+                                       <genre authority="marcgt">filmography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'r')">
+                                       <genre authority="marcgt">directory</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'s')">
+                                       <genre authority="marcgt">statistics</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'t')">
+                                       <genre authority="marcgt">technical report</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'v')">
+                                       <genre authority="marcgt">legal case and case notes</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'w')">
+                                       <genre authority="marcgt">law report or digest</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'z')">
+                                       <genre authority="marcgt">treaty</genre>
+                               </xsl:when>
+                       </xsl:choose>
+                       <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"/>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-29='1'">
+                                       <genre authority="marcgt">conference publication</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='CF'">
+                       <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"/>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-26='a'">
+                                       <genre authority="marcgt">numeric data</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='e'">
+                                       <genre authority="marcgt">database</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='f'">
+                                       <genre authority="marcgt">font</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='g'">
+                                       <genre authority="marcgt">game</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='BK'">
+                       <xsl:if test="substring($controlField008,25,1)='j'">
+                               <genre authority="marcgt">patent</genre>
+                       </xsl:if>
+                       <xsl:if test="substring($controlField008,25,1)='2'">
+                               <genre authority="marcgt">offprint</genre>
+                       </xsl:if>
+                       <xsl:if test="substring($controlField008,31,1)='1'">
+                               <genre authority="marcgt">festschrift</genre>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"/>
+                       <xsl:if
+                               test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
+                               <genre authority="marcgt">biography</genre>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"/>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-33='e'">
+                                       <genre authority="marcgt">essay</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='d'">
+                                       <genre authority="marcgt">drama</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='c'">
+                                       <genre authority="marcgt">comic strip</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='l'">
+                                       <genre authority="marcgt">fiction</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='h'">
+                                       <genre authority="marcgt">humor, satire</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='i'">
+                                       <genre authority="marcgt">letter</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='f'">
+                                       <genre authority="marcgt">novel</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='j'">
+                                       <genre authority="marcgt">short story</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='s'">
+                                       <genre authority="marcgt">speech</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='MU'">
+                       <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"/>
+                       <xsl:if test="contains($controlField008-30-31,'b')">
+                               <genre authority="marcgt">biography</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'c')">
+                               <genre authority="marcgt">conference publication</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'d')">
+                               <genre authority="marcgt">drama</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'e')">
+                               <genre authority="marcgt">essay</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'f')">
+                               <genre authority="marcgt">fiction</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'o')">
+                               <genre authority="marcgt">folktale</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'h')">
+                               <genre authority="marcgt">history</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'k')">
+                               <genre authority="marcgt">humor, satire</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'m')">
+                               <genre authority="marcgt">memoir</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'p')">
+                               <genre authority="marcgt">poetry</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'r')">
+                               <genre authority="marcgt">rehearsal</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'g')">
+                               <genre authority="marcgt">reporting</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'s')">
+                               <genre authority="marcgt">sound</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'l')">
+                               <genre authority="marcgt">speech</genre>
+                       </xsl:if>
+               </xsl:if>
+               <xsl:if test="$typeOf008='VM'">
+                       <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"/>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-33='a'">
+                                       <genre authority="marcgt">art original</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='b'">
+                                       <genre authority="marcgt">kit</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='c'">
+                                       <genre authority="marcgt">art reproduction</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='d'">
+                                       <genre authority="marcgt">diorama</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='f'">
+                                       <genre authority="marcgt">filmstrip</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='g'">
+                                       <genre authority="marcgt">legal article</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='i'">
+                                       <genre authority="marcgt">picture</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='k'">
+                                       <genre authority="marcgt">graphic</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='l'">
+                                       <genre authority="marcgt">technical drawing</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='m'">
+                                       <genre authority="marcgt">motion picture</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='n'">
+                                       <genre authority="marcgt">chart</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='o'">
+                                       <genre authority="marcgt">flash card</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='p'">
+                                       <genre authority="marcgt">microscope slide</genre>
+                               </xsl:when>
+                               <xsl:when
+                                       test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
+                                       <genre authority="marcgt">model</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='r'">
+                                       <genre authority="marcgt">realia</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='s'">
+                                       <genre authority="marcgt">slide</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='t'">
+                                       <genre authority="marcgt">transparency</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='v'">
+                                       <genre authority="marcgt">videorecording</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='w'">
+                                       <genre authority="marcgt">toy</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+
+               <!-- 1.20 047 genre tmee-->
+
+               <xsl:for-each select="marc:datafield[@tag=047]">
+                       <genre authority="marcgt">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"/>
+                               </xsl:attribute>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abcdef</xsl:with-param>
+                                       <xsl:with-param name="delimeter">-</xsl:with-param>
+                               </xsl:call-template>
+                       </genre>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=655]">
+                       <genre authority="marcgt">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"/>
+                               </xsl:attribute>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abvxyz</xsl:with-param>
+                                       <xsl:with-param name="delimeter">-</xsl:with-param>
+                               </xsl:call-template>
+                       </genre>
+               </xsl:for-each>
+               <originInfo>
+                       <xsl:variable name="MARCpublicationCode"
+                               select="normalize-space(substring($controlField008,16,3))"/>
+                       <xsl:if test="translate($MARCpublicationCode,'|','')">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">code</xsl:attribute>
+                                               <xsl:attribute name="authority">marccountry</xsl:attribute>
+                                               <xsl:value-of select="$MARCpublicationCode"/>
+                                       </placeTerm>
+                               </place>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">code</xsl:attribute>
+                                               <xsl:attribute name="authority">iso3166</xsl:attribute>
+                                               <xsl:value-of select="."/>
+                                       </placeTerm>
+                               </place>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">text</xsl:attribute>
+                                               <xsl:call-template name="chopPunctuationFront">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="chopPunctuation">
+                                                                       <xsl:with-param name="chopString" select="."/>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </placeTerm>
+                               </place>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
+                               <dateValid point="start">
+                                       <xsl:value-of select="."/>
+                               </dateValid>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
+                               <dateValid point="end">
+                                       <xsl:value-of select="."/>
+                               </dateValid>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
+                               <dateModified>
+                                       <xsl:value-of select="."/>
+                               </dateModified>
+                       </xsl:for-each>
+                       <xsl:for-each
+                               select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
+                               <xsl:choose>
+                                       <xsl:when test="@code='b'">
+                                               <publisher>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                               <xsl:with-param name="punctuation">
+                                                                       <xsl:text>:,;/ </xsl:text>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </publisher>
+                                       </xsl:when>
+                                       <xsl:when test="@code='c'">
+                                               <dateIssued>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </dateIssued>
+                                       </xsl:when>
+                                       <xsl:when test="@code='g'">
+                                               <dateCreated>
+                                                       <xsl:value-of select="."/>
+                                               </dateCreated>
+                                       </xsl:when>
+                               </xsl:choose>
+                       </xsl:for-each>
+                       <xsl:variable name="dataField260c">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString"
+                                               select="marc:datafield[@tag=260]/marc:subfield[@code='c']"/>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <xsl:variable name="controlField008-7-10"
+                               select="normalize-space(substring($controlField008, 8, 4))"/>
+                       <xsl:variable name="controlField008-11-14"
+                               select="normalize-space(substring($controlField008, 12, 4))"/>
+                       <xsl:variable name="controlField008-6"
+                               select="normalize-space(substring($controlField008, 7, 1))"/>
+                       <xsl:if
+                               test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
+                               <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
+                                       <dateIssued encoding="marc">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if
+                               test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
+                               <xsl:if test="$controlField008-7-10">
+                                       <dateIssued encoding="marc" point="start">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if
+                               test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <dateIssued encoding="marc" point="end">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='q'">
+                               <xsl:if test="$controlField008-7-10">
+                                       <dateIssued encoding="marc" point="start" qualifier="questionable">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='q'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <dateIssued encoding="marc" point="end" qualifier="questionable">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='t'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <copyrightDate encoding="marc">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </copyrightDate>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:for-each
+                               select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
+                               <dateCaptured encoding="iso8601">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
+                               <dateCaptured encoding="iso8601" point="start">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
+                               <dateCaptured encoding="iso8601" point="end">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
+                               <edition>
+                                       <xsl:value-of select="."/>
+                               </edition>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:leader">
+                               <issuance>
+                                       <xsl:choose>
+                                               <xsl:when
+                                                       test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'"
+                                                       >monographic</xsl:when>
+                                               <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'"
+                                               >continuing</xsl:when>
+                                       </xsl:choose>
+                               </issuance>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
+                               <frequency>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">ab</xsl:with-param>
+                                       </xsl:call-template>
+                               </frequency>
+                       </xsl:for-each>
+               </originInfo>
+               <xsl:variable name="controlField008-35-37"
+                       select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"/>
+               <xsl:if test="$controlField008-35-37">
+                       <language>
+                               <languageTerm authority="iso639-2b" type="code">
+                                       <xsl:value-of select="substring($controlField008,36,3)"/>
+                               </languageTerm>
+                       </language>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=041]">
+                       <xsl:for-each
+                               select="marc:subfield[@code='a' or @code='b' or @code='d' or @code='e' or @code='f' or @code='g' or @code='h']">
+                               <xsl:variable name="langCodes" select="."/>
+                               <xsl:choose>
+                                       <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
+                                               <!-- not stacked but could be repeated -->
+                                               <xsl:call-template name="rfcLanguages">
+                                                       <xsl:with-param name="nodeNum">
+                                                               <xsl:value-of select="1"/>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="usedLanguages">
+                                                               <xsl:text/>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="controlField008-35-37">
+                                                               <xsl:value-of select="$controlField008-35-37"/>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <!-- iso -->
+                                               <xsl:variable name="allLanguages">
+                                                       <xsl:copy-of select="$langCodes"/>
+                                               </xsl:variable>
+                                               <xsl:variable name="currentLanguage">
+                                                       <xsl:value-of select="substring($allLanguages,1,3)"/>
+                                               </xsl:variable>
+                                               <xsl:call-template name="isoLanguage">
+                                                       <xsl:with-param name="currentLanguage">
+                                                               <xsl:value-of select="substring($allLanguages,1,3)"/>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="remainingLanguages">
+                                                               <xsl:value-of
+                                                                       select="substring($allLanguages,4,string-length($allLanguages)-3)"
+                                                               />
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="usedLanguages">
+                                                               <xsl:if test="$controlField008-35-37">
+                                                                       <xsl:value-of select="$controlField008-35-37"/>
+                                                               </xsl:if>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </xsl:for-each>
+               </xsl:for-each>
+               <xsl:variable name="physicalDescription">
+                       <!--3.2 change tmee 007/11 -->
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
+                               <digitalOrigin>reformatted digital</digitalOrigin>
+                       </xsl:if>
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
+                               <digitalOrigin>digitized microfilm</digitalOrigin>
+                       </xsl:if>
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
+                               <digitalOrigin>digitized other analog</digitalOrigin>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"/>
+                       <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"/>
+                       <xsl:variable name="check008-23">
+                               <xsl:if
+                                       test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
+                                       <xsl:value-of select="true()"/>
+                               </xsl:if>
+                       </xsl:variable>
+                       <xsl:variable name="check008-29">
+                               <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
+                                       <xsl:value-of select="true()"/>
+                               </xsl:if>
+                       </xsl:variable>
+                       <xsl:choose>
+                               <xsl:when
+                                       test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
+                                       <form authority="marcform">braille</form>
+                               </xsl:when>
+                               <xsl:when
+                                       test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
+                                       <form authority="marcform">print</form>
+                               </xsl:when>
+                               <xsl:when
+                                       test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
+                                       <form authority="marcform">electronic</form>
+                               </xsl:when>
+                               <xsl:when
+                                       test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
+                                       <form authority="marcform">microfiche</form>
+                               </xsl:when>
+                               <xsl:when
+                                       test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
+                                       <form authority="marcform">microfilm</form>
+                               </xsl:when>
+                       </xsl:choose>
+                       <!-- 1/04 fix -->
+                       <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"
+                                                       />
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"
+                                                       />
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"
+                                                       />
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"
+                                                       />
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"
+                                                       />
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"
+                                                       />
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
+                               <form>
+                                       <xsl:value-of select="."/>
+                               </form>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
+                               <xsl:choose>
+                                       <xsl:when test="substring(text(),14,1)='a'">
+                                               <reformattingQuality>access</reformattingQuality>
+                                       </xsl:when>
+                                       <xsl:when test="substring(text(),14,1)='p'">
+                                               <reformattingQuality>preservation</reformattingQuality>
+                                       </xsl:when>
+                                       <xsl:when test="substring(text(),14,1)='r'">
+                                               <reformattingQuality>replacement</reformattingQuality>
+                                       </xsl:when>
+                               </xsl:choose>
+                       </xsl:for-each>
+                       <!--3.2 change tmee 007/01 -->
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
+                               <form authority="smd">chip cartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
+                               <form authority="smd">computer optical disc cartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
+                               <form authority="smd">magnetic disc</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
+                               <form authority="smd">magneto-optical disc</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
+                               <form authority="smd">optical disc</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
+                               <form authority="smd">tape cartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
+                               <form authority="smd">tape cassette</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
+                               <form authority="smd">tape reel</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
+                               <form authority="smd">celestial globe</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
+                               <form authority="smd">earth moon globe</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
+                               <form authority="smd">planetary or lunar globe</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
+                               <form authority="smd">terrestrial globe</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
+                               <form authority="smd">kit</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
+                               <form authority="smd">atlas</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
+                               <form authority="smd">diagram</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
+                               <form authority="smd">map</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
+                               <form authority="smd">model</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
+                               <form authority="smd">profile</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote-sensing image</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
+                               <form authority="smd">section</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
+                               <form authority="smd">view</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
+                               <form authority="smd">aperture card</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
+                               <form authority="smd">microfiche</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
+                               <form authority="smd">microfiche cassette</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
+                               <form authority="smd">microfilm cartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
+                               <form authority="smd">microfilm cassette</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
+                               <form authority="smd">microfilm reel</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
+                               <form authority="smd">microopaque</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
+                               <form authority="smd">film cartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
+                               <form authority="smd">film cassette</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
+                               <form authority="smd">film reel</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
+                               <form authority="smd">chart</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
+                               <form authority="smd">collage</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
+                               <form authority="smd">drawing</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
+                               <form authority="smd">flash card</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
+                               <form authority="smd">painting</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
+                               <form authority="smd">photomechanical print</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
+                               <form authority="smd">photonegative</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
+                               <form authority="smd">photoprint</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
+                               <form authority="smd">picture</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
+                               <form authority="smd">print</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
+                               <form authority="smd">technical drawing</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
+                               <form authority="smd">notated music</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
+                               <form authority="smd">filmslip</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
+                               <form authority="smd">filmstrip cartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
+                               <form authority="smd">filmstrip roll</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
+                               <form authority="smd">other filmstrip type</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
+                               <form authority="smd">slide</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
+                               <form authority="smd">transparency</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote-sensing image</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
+                               <form authority="smd">cylinder</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
+                               <form authority="smd">roll</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
+                               <form authority="smd">sound cartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
+                               <form authority="smd">sound cassette</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
+                               <form authority="smd">sound disc</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
+                               <form authority="smd">sound-tape reel</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
+                               <form authority="smd">sound-track film</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
+                               <form authority="smd">wire recording</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
+                               <form authority="smd">braille</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
+                               <form authority="smd">combination</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
+                               <form authority="smd">moon</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
+                               <form authority="smd">tactile, with no writing system</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
+                               <form authority="smd">braille</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
+                               <form authority="smd">large print</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
+                               <form authority="smd">regular print</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
+                               <form authority="smd">text in looseleaf binder</form>
+                       </xsl:if>
+
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
+                               <form authority="smd">videocartridge</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
+                               <form authority="smd">videocassette</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
+                               <form authority="smd">videodisc</form>
+                       </xsl:if>
+                       <xsl:if
+                               test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
+                               <form authority="smd">videoreel</form>
+                       </xsl:if>
+
+                       <xsl:for-each
+                               select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)&gt;1]">
+                               <internetMediaType>
+                                       <xsl:value-of select="."/>
+                               </internetMediaType>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=300]">
+                               <extent>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abce</xsl:with-param>
+                                       </xsl:call-template>
+                               </extent>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($physicalDescription))">
+                       <physicalDescription>
+                               <xsl:copy-of select="$physicalDescription"/>
+                       </physicalDescription>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=520]">
+                       <abstract>
+                               <xsl:call-template name="uri"/>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </abstract>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=505]">
+                       <tableOfContents>
+                               <xsl:call-template name="uri"/>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">agrt</xsl:with-param>
+                               </xsl:call-template>
+                       </tableOfContents>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=521]">
+                       <targetAudience>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </targetAudience>
+               </xsl:for-each>
+               <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
+                       <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"/>
+                       <xsl:choose>
+                               <!-- 01/04 fix -->
+                               <xsl:when test="$controlField008-22='d'">
+                                       <targetAudience authority="marctarget">adolescent</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='e'">
+                                       <targetAudience authority="marctarget">adult</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='g'">
+                                       <targetAudience authority="marctarget">general</targetAudience>
+                               </xsl:when>
+                               <xsl:when
+                                       test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
+                                       <targetAudience authority="marctarget">juvenile</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='a'">
+                                       <targetAudience authority="marctarget">preschool</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='f'">
+                                       <targetAudience authority="marctarget">specialized</targetAudience>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
+                       <note type="statement of responsibility">
+                               <xsl:value-of select="."/>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=500]">
+                       <note>
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                               <xsl:call-template name="uri"/>
+                       </note>
+               </xsl:for-each>
+
+               <!--3.2 change tmee additional note fields-->
+
+               <xsl:for-each select="marc:datafield[@tag=506]">
+                       <note type="restrictions">
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+
+               <xsl:for-each select="marc:datafield[@tag=510]">
+                       <note type="citation/reference">
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+
+
+               <xsl:for-each select="marc:datafield[@tag=511]">
+                       <note type="performers">
+                               <xsl:call-template name="uri"/>
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=518]">
+                       <note type="venue">
+                               <xsl:call-template name="uri"/>
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </note>
+               </xsl:for-each>
+
+               <xsl:for-each select="marc:datafield[@tag=530]">
+                       <note type="additional physical form">
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+
+               <xsl:for-each select="marc:datafield[@tag=533]">
+                       <note type="reproduction">
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+
+               <xsl:for-each select="marc:datafield[@tag=534]">
+                       <note type="original version">
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+
+               <xsl:for-each select="marc:datafield[@tag=538]">
+                       <note type="system details">
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+
+               <xsl:for-each select="marc:datafield[@tag=583]">
+                       <note type="action">
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+
+               <xsl:for-each
+                       select="marc:datafield[@tag=501 or @tag=502 or @tag=504 or @tag=507 or @tag=508 or  @tag=513 or @tag=514 or @tag=515 or @tag=516 or @tag=522 or @tag=524 or @tag=525 or @tag=526 or @tag=535 or @tag=536 or @tag=540 or @tag=541 or @tag=544 or @tag=545 or @tag=546 or @tag=547 or @tag=550 or @tag=552 or @tag=555 or @tag=556 or @tag=561 or @tag=562 or @tag=565 or @tag=567 or @tag=580 or @tag=581 or @tag=584 or @tag=585 or @tag=586]">
+                       <note>
+                               <xsl:call-template name="uri"/>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."/>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each
+                       select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
+                       <subject>
+                               <cartographics>
+                                       <coordinates>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">defg</xsl:with-param>
+                                               </xsl:call-template>
+                                       </coordinates>
+                               </cartographics>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=043]">
+                       <subject>
+                               <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
+                                       <geographicCode>
+                                               <xsl:attribute name="authority">
+                                                       <xsl:if test="@code='a'">
+                                                               <xsl:text>marcgac</xsl:text>
+                                                       </xsl:if>
+                                                       <xsl:if test="@code='b'">
+                                                               <xsl:value-of select="following-sibling::marc:subfield[@code=2]"/>
+                                                       </xsl:if>
+                                                       <xsl:if test="@code='c'">
+                                                               <xsl:text>iso3166</xsl:text>
+                                                       </xsl:if>
+                                               </xsl:attribute>
+                                               <xsl:value-of select="self::marc:subfield"/>
+                                       </geographicCode>
+                               </xsl:for-each>
+                       </subject>
+               </xsl:for-each>
+               <!-- tmee 2006/11/27 -->
+               <xsl:for-each select="marc:datafield[@tag=255]">
+                       <subject>
+                               <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
+                                       <cartographics>
+                                               <xsl:if test="@code='a'">
+                                                       <scale>
+                                                               <xsl:value-of select="."/>
+                                                       </scale>
+                                               </xsl:if>
+                                               <xsl:if test="@code='b'">
+                                                       <projection>
+                                                               <xsl:value-of select="."/>
+                                                       </projection>
+                                               </xsl:if>
+                                               <xsl:if test="@code='c'">
+                                                       <coordinates>
+                                                               <xsl:value-of select="."/>
+                                                       </coordinates>
+                                               </xsl:if>
+                                       </cartographics>
+                               </xsl:for-each>
+                       </subject>
+               </xsl:for-each>
+
+               <xsl:apply-templates select="marc:datafield[653 &gt;= @tag and @tag &gt;= 600]"/>
+               <xsl:apply-templates select="marc:datafield[@tag=656]"/>
+               <xsl:for-each select="marc:datafield[@tag=752 or @tag=662]">
+                       <subject>
+                               <hierarchicalGeographic>
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <country>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </country>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <state>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </state>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='c']">
+                                               <county>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </county>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='d']">
+                                               <city>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </city>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='e']">
+                                               <citySection>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </citySection>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='g']">
+                                               <region>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </region>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='h']">
+                                               <extraterrestrialArea>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </extraterrestrialArea>
+                                       </xsl:for-each>
+                               </hierarchicalGeographic>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
+                       <subject>
+                               <xsl:choose>
+                                       <xsl:when test="@ind1=2">
+                                               <temporal encoding="iso8601" point="start">
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString">
+                                                                       <xsl:value-of select="marc:subfield[@code='b'][1]"/>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </temporal>
+                                               <temporal encoding="iso8601" point="end">
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString">
+                                                                       <xsl:value-of select="marc:subfield[@code='b'][2]"/>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </temporal>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:for-each select="marc:subfield[@code='b']">
+                                                       <temporal encoding="iso8601">
+                                                               <xsl:call-template name="chopPunctuation">
+                                                                       <xsl:with-param name="chopString" select="."/>
+                                                               </xsl:call-template>
+                                                       </temporal>
+                                               </xsl:for-each>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=050]">
+                       <xsl:for-each select="marc:subfield[@code='b']">
+                               <classification authority="lcc">
+                                       <xsl:if test="../marc:subfield[@code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:value-of select="../marc:subfield[@code='3']"/>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"/>
+                                       <xsl:text> </xsl:text>
+                                       <xsl:value-of select="text()"/>
+                               </classification>
+                       </xsl:for-each>
+                       <xsl:for-each
+                               select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
+                               <classification authority="lcc">
+                                       <xsl:if test="../marc:subfield[@code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:value-of select="../marc:subfield[@code='3']"/>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="text()"/>
+                               </classification>
+                       </xsl:for-each>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=082]">
+                       <classification authority="ddc">
+                               <xsl:if test="marc:subfield[@code='2']">
+                                       <xsl:attribute name="edition">
+                                               <xsl:value-of select="marc:subfield[@code='2']"/>
+                                       </xsl:attribute>
+                               </xsl:if>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=080]">
+                       <classification authority="udc">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abx</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=060]">
+                       <classification authority="nlm">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
+                       <classification authority="sudocs">
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
+                       <classification authority="candoc">
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086]">
+                       <classification>
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"/>
+                               </xsl:attribute>
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=084]">
+                       <classification>
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"/>
+                               </xsl:attribute>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=440]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">av</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">av</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=510]">
+                       <relatedItem type="isReferencedBy">
+                               <note>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abcx3</xsl:with-param>
+                                       </xsl:call-template>
+                               </note>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=534]">
+                       <relatedItem type="original">
+                               <xsl:call-template name="relatedTitle"/>
+                               <xsl:call-template name="relatedName"/>
+                               <xsl:if test="marc:subfield[@code='b' or @code='c']">
+                                       <originInfo>
+                                               <xsl:for-each select="marc:subfield[@code='c']">
+                                                       <publisher>
+                                                               <xsl:value-of select="."/>
+                                                       </publisher>
+                                               </xsl:for-each>
+                                               <xsl:for-each select="marc:subfield[@code='b']">
+                                                       <edition>
+                                                               <xsl:value-of select="."/>
+                                                       </edition>
+                                               </xsl:for-each>
+                                       </originInfo>
+                               </xsl:if>
+                               <xsl:call-template name="relatedIdentifierISSN"/>
+                               <xsl:for-each select="marc:subfield[@code='z']">
+                                       <identifier type="isbn">
+                                               <xsl:value-of select="."/>
+                                       </identifier>
+                               </xsl:for-each>
+                               <xsl:call-template name="relatedNote"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"/>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <name type="personal">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aq</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">g</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="termsOfAddress"/>
+                                       <xsl:call-template name="nameDate"/>
+                                       <xsl:call-template name="role"/>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                               <xsl:call-template name="relatedIdentifierISSN"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"/>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">dg</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </titleInfo>
+                               <name type="corporate">
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <namePart>
+                                                       <xsl:value-of select="."/>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <namePart>
+                                                       <xsl:value-of select="."/>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:variable name="tempNamePart">
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">c</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:variable>
+                                       <xsl:if test="normalize-space($tempNamePart)">
+                                               <namePart>
+                                                       <xsl:value-of select="$tempNamePart"/>
+                                               </namePart>
+                                       </xsl:if>
+                                       <xsl:call-template name="role"/>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                               <xsl:call-template name="relatedIdentifierISSN"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"/>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </titleInfo>
+                               <name type="conference">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">gn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                               <xsl:call-template name="relatedIdentifierISSN"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"/>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"/>
+                               <xsl:call-template name="relatedIdentifierISSN"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"/>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
+                       <relatedItem type="series">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each
+                       select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
+                       <relatedItem>
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=775]">
+                       <relatedItem type="otherVersion">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
+                       <relatedItem type="constituent">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
+                       <relatedItem type="host">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=776]">
+                       <relatedItem type="otherFormat">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=780]">
+                       <relatedItem type="preceding">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=785]">
+                       <relatedItem type="succeeding">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=786]">
+                       <relatedItem type="original">
+                               <xsl:call-template name="relatedItem76X-78X"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=800]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <name type="personal">
+                                       <namePart>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">aq</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="beforeCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="termsOfAddress"/>
+                                       <xsl:call-template name="nameDate"/>
+                                       <xsl:call-template name="role"/>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=810]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">dg</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </titleInfo>
+                               <name type="corporate">
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <namePart>
+                                                       <xsl:value-of select="."/>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <namePart>
+                                                       <xsl:value-of select="."/>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">c</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="role"/>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=811]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </titleInfo>
+                               <name type="conference">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">gn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="role"/>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='830']">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
+                       <relatedItem>
+                               <internetMediaType>
+                                       <xsl:value-of select="."/>
+                               </internetMediaType>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='020']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">isbn</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="isbn">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">isrc</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="isrc">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">ismn</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="ismn">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">sici</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="sici">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='022']">
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <xsl:call-template name="isInvalid">
+                                       <xsl:with-param name="type">issn</xsl:with-param>
+                               </xsl:call-template>
+                               <identifier type="issn">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+                       <xsl:if test="marc:subfield[@code='l']">
+                               <xsl:call-template name="isInvalid">
+                                       <xsl:with-param name="type">issn-l</xsl:with-param>
+                               </xsl:call-template>
+                               <identifier type="issn-l">
+                                       <xsl:value-of select="marc:subfield[@code='l']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+
+
+
+               <xsl:for-each select="marc:datafield[@tag='010']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">lccn</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="lccn">
+                               <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='028']">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind1='0'">issue number</xsl:when>
+                                               <xsl:when test="@ind1='1'">matrix number</xsl:when>
+                                               <xsl:when test="@ind1='2'">music plate</xsl:when>
+                                               <xsl:when test="@ind1='3'">music publisher</xsl:when>
+                                               <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
+                                       </xsl:choose>
+                               </xsl:attribute>
+                               <!--<xsl:call-template name="isInvalid"/>-->
+                               <!-- no $z in 028 -->
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">
+                                               <xsl:choose>
+                                                       <xsl:when test="@ind1='0'">ba</xsl:when>
+                                                       <xsl:otherwise>ab</xsl:otherwise>
+                                               </xsl:choose>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='037']">
+                       <identifier type="stock number">
+                               <!--<xsl:call-template name="isInvalid"/>-->
+                               <!-- no $z in 037 -->
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:choose>
+                                               <xsl:when
+                                                       test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')"
+                                                       >doi</xsl:when>
+                                               <xsl:when
+                                                       test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov')"
+                                                       >hdl</xsl:when>
+                                               <xsl:otherwise>uri</xsl:otherwise>
+                                       </xsl:choose>
+                               </xsl:attribute>
+                               <xsl:choose>
+                                       <xsl:when
+                                               test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov') ">
+                                               <xsl:value-of
+                                                       select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"
+                                               />
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:value-of select="marc:subfield[@code='u']"/>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </identifier>
+                       <xsl:if
+                               test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
+                               <identifier type="hdl">
+                                       <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">y3z</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of
+                                               select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"
+                                       />
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
+                       <identifier type="upc">
+                               <xsl:call-template name="isInvalid"/>
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </identifier>
+               </xsl:for-each>
+
+               <!-- 1/04 fix added $y -->
+
+               <!-- 1.21  tmee-->
+               <xsl:for-each select="marc:datafield[@tag=856][@ind2=1][marc:subfield[@code='u']]">
+                       <relatedItem type="otherVersion">
+                               <location>
+                                       <url>
+                                               <xsl:if test="marc:subfield[@code='y' or @code='3']">
+                                                       <xsl:attribute name="displayLabel">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">y3</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:attribute>
+                                               </xsl:if>
+                                               <xsl:if test="marc:subfield[@code='z' ]">
+                                                       <xsl:attribute name="note">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">z</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:attribute>
+                                               </xsl:if>
+                                               <xsl:value-of select="marc:subfield[@code='u']"/>
+                                       </url>
+                               </location>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=856][@ind2=2][marc:subfield[@code='u']]">
+                       <relatedItem>
+                               <location>
+                                       <url>
+                                               <xsl:if test="marc:subfield[@code='y' or @code='3']">
+                                                       <xsl:attribute name="displayLabel">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">y3</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:attribute>
+                                               </xsl:if>
+                                               <xsl:if test="marc:subfield[@code='z' ]">
+                                                       <xsl:attribute name="note">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">z</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:attribute>
+                                               </xsl:if>
+                                               <xsl:value-of select="marc:subfield[@code='u']"/>
+                                       </url>
+                               </location>
+                       </relatedItem>
+               </xsl:for-each>
+
+               <!-- 3.2 change tmee 856z  -->
+
+               <!-- 1.24  tmee  -->
+               <xsl:for-each select="marc:datafield[@tag=852]">
+                       <location>
+                               <xsl:if test="marc:subfield[@code='a' or @code='b' or @code='e']">
+                                       <physicalLocation>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">abe</xsl:with-param>
+                                               </xsl:call-template>
+                                       </physicalLocation>
+                               </xsl:if>
+
+                               <xsl:if test="marc:subfield[@code='u']">
+                                       <physicalLocation>
+                                               <xsl:call-template name="uri"/>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">u</xsl:with-param>
+                                               </xsl:call-template>
+                                       </physicalLocation>
+                               </xsl:if>
+
+                               <xsl:if
+                                       test="marc:subfield[@code='h' or @code='i' or @code='j' or @code='k' or @code='l' or @code='m' or @code='t']">
+                                       <shelfLocation>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">hijklmt</xsl:with-param>
+                                               </xsl:call-template>
+                                       </shelfLocation>
+                               </xsl:if>
+                       </location>
+               </xsl:for-each>
+
+               <xsl:for-each select="marc:datafield[@tag=506]">
+                       <accessCondition type="restrictionOnAccess">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abcd35</xsl:with-param>
+                               </xsl:call-template>
+                       </accessCondition>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=540]">
+                       <accessCondition type="useAndReproduction">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abcde35</xsl:with-param>
+                               </xsl:call-template>
+                       </accessCondition>
+               </xsl:for-each>
+
+               <recordInfo>
+                       <!-- 1.25  tmee-->
+
+
+                       <xsl:for-each select="marc:leader[substring($leader,19,1)='a']">
+                               <descriptionStandard>aacr2</descriptionStandard>
+                       </xsl:for-each>
+
+                       <xsl:for-each select="marc:datafield[@tag=040]">
+                               <xsl:if test="marc:subfield[@code='e']">
+                                       <descriptionStandard>
+                                               <xsl:value-of select="marc:subfield[@code='e']"/>
+                                       </descriptionStandard>
+                               </xsl:if>
+                               <recordContentSource authority="marcorg">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </recordContentSource>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=008]">
+                               <recordCreationDate encoding="marc">
+                                       <xsl:value-of select="substring(.,1,6)"/>
+                               </recordCreationDate>
+                       </xsl:for-each>
+
+                       <xsl:for-each select="marc:controlfield[@tag=005]">
+                               <recordChangeDate encoding="iso8601">
+                                       <xsl:value-of select="."/>
+                               </recordChangeDate>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=001]">
+                               <recordIdentifier>
+                                       <xsl:if test="../marc:controlfield[@tag=003]">
+                                               <xsl:attribute name="source">
+                                                       <xsl:value-of select="../marc:controlfield[@tag=003]"/>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="."/>
+                               </recordIdentifier>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
+                               <languageOfCataloging>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="."/>
+                                       </languageTerm>
+                               </languageOfCataloging>
+                       </xsl:for-each>
+               </recordInfo>
+       </xsl:template>
+       <xsl:template name="displayForm">
+               <xsl:for-each select="marc:subfield[@code='c']">
+                       <displayForm>
+                               <xsl:value-of select="."/>
+                       </displayForm>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="affiliation">
+               <xsl:for-each select="marc:subfield[@code='u']">
+                       <affiliation>
+                               <xsl:value-of select="."/>
+                       </affiliation>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="uri">
+               <xsl:for-each select="marc:subfield[@code='u']">
+                       <xsl:attribute name="xlink:href">
+                               <xsl:value-of select="."/>
+                       </xsl:attribute>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='0']">
+                       <xsl:choose>
+                               <xsl:when test="contains(text(), ')')">
+                                       <xsl:attribute name="xlink:href">
+                                               <xsl:value-of select="substring-after(text(), ')')"></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:attribute name="xlink:href">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="role">
+               <xsl:for-each select="marc:subfield[@code='e']">
+                       <role>
+                               <roleTerm type="text">
+                                       <xsl:value-of select="."/>
+                               </roleTerm>
+                       </role>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='4']">
+                       <role>
+                               <roleTerm authority="marcrelator" type="code">
+                                       <xsl:value-of select="."/>
+                               </roleTerm>
+                       </role>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="part">
+               <xsl:variable name="partNumber">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">n</xsl:with-param>
+                               <xsl:with-param name="anyCodes">n</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:variable name="partName">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">p</xsl:with-param>
+                               <xsl:with-param name="anyCodes">p</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($partNumber))">
+                       <partNumber>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="$partNumber"/>
+                               </xsl:call-template>
+                       </partNumber>
+               </xsl:if>
+               <xsl:if test="string-length(normalize-space($partName))">
+                       <partName>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="$partName"/>
+                               </xsl:call-template>
+                       </partName>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedPart">
+               <xsl:if test="@tag=773">
+                       <xsl:for-each select="marc:subfield[@code='g']">
+                               <part>
+                                       <text>
+                                               <xsl:value-of select="."/>
+                                       </text>
+                               </part>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:subfield[@code='q']">
+                               <part>
+                                       <xsl:call-template name="parsePart"/>
+                               </part>
+                       </xsl:for-each>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedPartNumName">
+               <xsl:variable name="partNumber">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">g</xsl:with-param>
+                               <xsl:with-param name="anyCodes">g</xsl:with-param>
+                               <xsl:with-param name="afterCodes">pst</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:variable name="partName">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">p</xsl:with-param>
+                               <xsl:with-param name="anyCodes">p</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($partNumber))">
+                       <partNumber>
+                               <xsl:value-of select="$partNumber"/>
+                       </partNumber>
+               </xsl:if>
+               <xsl:if test="string-length(normalize-space($partName))">
+                       <partName>
+                               <xsl:value-of select="$partName"/>
+                       </partName>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedName">
+               <xsl:for-each select="marc:subfield[@code='a']">
+                       <name>
+                               <namePart>
+                                       <xsl:value-of select="."/>
+                               </namePart>
+                       </name>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedForm">
+               <xsl:for-each select="marc:subfield[@code='h']">
+                       <physicalDescription>
+                               <form>
+                                       <xsl:value-of select="."/>
+                               </form>
+                       </physicalDescription>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedExtent">
+               <xsl:for-each select="marc:subfield[@code='h']">
+                       <physicalDescription>
+                               <extent>
+                                       <xsl:value-of select="."/>
+                               </extent>
+                       </physicalDescription>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedNote">
+               <xsl:for-each select="marc:subfield[@code='n']">
+                       <note>
+                               <xsl:value-of select="."/>
+                       </note>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedSubject">
+               <xsl:for-each select="marc:subfield[@code='j']">
+                       <subject>
+                               <temporal encoding="iso8601">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString" select="."/>
+                                       </xsl:call-template>
+                               </temporal>
+                       </subject>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifierISSN">
+               <xsl:for-each select="marc:subfield[@code='x']">
+                       <identifier type="issn">
+                               <xsl:value-of select="."/>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifierLocal">
+               <xsl:for-each select="marc:subfield[@code='w']">
+                       <identifier type="local">
+                               <xsl:value-of select="."/>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifier">
+               <xsl:for-each select="marc:subfield[@code='o']">
+                       <identifier>
+                               <xsl:value-of select="."/>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedItem76X-78X">
+               <xsl:call-template name="displayLabel"/>
+               <xsl:call-template name="relatedTitle76X-78X"/>
+               <xsl:call-template name="relatedName"/>
+               <xsl:call-template name="relatedOriginInfo"/>
+               <xsl:call-template name="relatedLanguage"/>
+               <xsl:call-template name="relatedExtent"/>
+               <xsl:call-template name="relatedNote"/>
+               <xsl:call-template name="relatedSubject"/>
+               <xsl:call-template name="relatedIdentifier"/>
+               <xsl:call-template name="relatedIdentifierISSN"/>
+               <xsl:call-template name="relatedIdentifierLocal"/>
+               <xsl:call-template name="relatedPart"/>
+       </xsl:template>
+       <xsl:template name="subjectGeographicZ">
+               <geographic>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."/>
+                       </xsl:call-template>
+               </geographic>
+       </xsl:template>
+       <xsl:template name="subjectTemporalY">
+               <temporal>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."/>
+                       </xsl:call-template>
+               </temporal>
+       </xsl:template>
+       <xsl:template name="subjectTopic">
+               <topic>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."/>
+                       </xsl:call-template>
+               </topic>
+       </xsl:template>
+       <!-- 3.2 change tmee 6xx $v genre -->
+       <xsl:template name="subjectGenre">
+               <genre>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."/>
+                       </xsl:call-template>
+               </genre>
+       </xsl:template>
+
+       <xsl:template name="nameABCDN">
+               <xsl:for-each select="marc:subfield[@code='a']">
+                       <namePart>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="."/>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='b']">
+                       <namePart>
+                               <xsl:value-of select="."/>
+                       </namePart>
+               </xsl:for-each>
+               <xsl:if
+                       test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
+                       <namePart>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">cdn</xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="nameABCDQ">
+               <namePart>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString">
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">aq</xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:with-param>
+                               <xsl:with-param name="punctuation">
+                                       <xsl:text>:,;/ </xsl:text>
+                               </xsl:with-param>
+                       </xsl:call-template>
+               </namePart>
+               <xsl:call-template name="termsOfAddress"/>
+               <xsl:call-template name="nameDate"/>
+       </xsl:template>
+       <xsl:template name="nameACDEQ">
+               <namePart>
+                       <xsl:call-template name="subfieldSelect">
+                               <xsl:with-param name="codes">acdeq</xsl:with-param>
+                       </xsl:call-template>
+               </namePart>
+       </xsl:template>
+       <xsl:template name="constituentOrRelatedType">
+               <xsl:if test="@ind2=2">
+                       <xsl:attribute name="type">constituent</xsl:attribute>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedTitle">
+               <xsl:for-each select="marc:subfield[@code='t']">
+                       <titleInfo>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                       </titleInfo>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedTitle76X-78X">
+               <xsl:for-each select="marc:subfield[@code='t']">
+                       <titleInfo>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='p']">
+                       <titleInfo type="abbreviated">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='s']">
+                       <titleInfo type="uniform">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedOriginInfo">
+               <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
+                       <originInfo>
+                               <xsl:if test="@tag=775">
+                                       <xsl:for-each select="marc:subfield[@code='f']">
+                                               <place>
+                                                       <placeTerm>
+                                                               <xsl:attribute name="type">code</xsl:attribute>
+                                                               <xsl:attribute name="authority">marcgac</xsl:attribute>
+                                                               <xsl:value-of select="."/>
+                                                       </placeTerm>
+                                               </place>
+                                       </xsl:for-each>
+                               </xsl:if>
+                               <xsl:for-each select="marc:subfield[@code='d']">
+                                       <publisher>
+                                               <xsl:value-of select="."/>
+                                       </publisher>
+                               </xsl:for-each>
+                               <xsl:for-each select="marc:subfield[@code='b']">
+                                       <edition>
+                                               <xsl:value-of select="."/>
+                                       </edition>
+                               </xsl:for-each>
+                       </originInfo>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedLanguage">
+               <xsl:for-each select="marc:subfield[@code='e']">
+                       <xsl:call-template name="getLanguage">
+                               <xsl:with-param name="langString">
+                                       <xsl:value-of select="."/>
+                               </xsl:with-param>
+                       </xsl:call-template>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="nameDate">
+               <xsl:for-each select="marc:subfield[@code='d']">
+                       <namePart type="date">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="."/>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="subjectAuthority">
+               <xsl:if test="@ind2!=4">
+                       <xsl:if test="@ind2!=' '">
+                               <xsl:if test="@ind2!=8">
+                                       <xsl:if test="@ind2!=9">
+                                               <xsl:attribute name="authority">
+                                                       <xsl:choose>
+                                                               <xsl:when test="@ind2=0">lcsh</xsl:when>
+                                                               <xsl:when test="@ind2=1">lcshac</xsl:when>
+                                                               <xsl:when test="@ind2=2">mesh</xsl:when>
+                                                               <!-- 1/04 fix -->
+                                                               <xsl:when test="@ind2=3">nal</xsl:when>
+                                                               <xsl:when test="@ind2=5">csh</xsl:when>
+                                                               <xsl:when test="@ind2=6">rvm</xsl:when>
+                                                               <xsl:when test="@ind2=7">
+                                                                       <xsl:value-of select="marc:subfield[@code='2']"/>
+                                                               </xsl:when>
+                                                       </xsl:choose>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                               </xsl:if>
+                       </xsl:if>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="subjectAnyOrder">
+               <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
+                       <xsl:choose>
+                               <xsl:when test="@code='v'">
+                                       <xsl:call-template name="subjectGenre"/>
+                               </xsl:when>
+                               <xsl:when test="@code='x'">
+                                       <xsl:call-template name="subjectTopic"/>
+                               </xsl:when>
+                               <xsl:when test="@code='y'">
+                                       <xsl:call-template name="subjectTemporalY"/>
+                               </xsl:when>
+                               <xsl:when test="@code='z'">
+                                       <xsl:call-template name="subjectGeographicZ"/>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="specialSubfieldSelect">
+               <xsl:param name="anyCodes"/>
+               <xsl:param name="axis"/>
+               <xsl:param name="beforeCodes"/>
+               <xsl:param name="afterCodes"/>
+               <xsl:variable name="str">
+                       <xsl:for-each select="marc:subfield">
+                               <xsl:if
+                                       test="contains($anyCodes, @code)      or (contains($beforeCodes,@code) and following-sibling::marc:subfield[@code=$axis])      or (contains($afterCodes,@code) and preceding-sibling::marc:subfield[@code=$axis])">
+                                       <xsl:value-of select="text()"/>
+                                       <xsl:text> </xsl:text>
+                               </xsl:if>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+       </xsl:template>
+
+       <!-- 3.2 change tmee 6xx $v genre -->
+       <xsl:template match="marc:datafield[@tag=600]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"/>
+                       <name type="personal">
+                               <xsl:call-template name="termsOfAddress"/>
+                               <namePart>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">aq</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </namePart>
+                               <xsl:call-template name="nameDate"/>
+                               <xsl:call-template name="affiliation"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"/>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=610]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"/>
+                       <name type="corporate">
+                               <xsl:for-each select="marc:subfield[@code='a']">
+                                       <namePart>
+                                               <xsl:value-of select="."/>
+                                       </namePart>
+                               </xsl:for-each>
+                               <xsl:for-each select="marc:subfield[@code='b']">
+                                       <namePart>
+                                               <xsl:value-of select="."/>
+                                       </namePart>
+                               </xsl:for-each>
+                               <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
+                                       <namePart>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">cdnp</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                               </xsl:if>
+                               <xsl:call-template name="role"/>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"/>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=611]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"/>
+                       <name type="conference">
+                               <namePart>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
+                                       </xsl:call-template>
+                               </namePart>
+                               <xsl:for-each select="marc:subfield[@code='4']">
+                                       <role>
+                                               <roleTerm authority="marcrelator" type="code">
+                                                       <xsl:value-of select="."/>
+                                               </roleTerm>
+                                       </role>
+                               </xsl:for-each>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"/>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=630]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"/>
+                       <titleInfo>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">adfhklor</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+                       <xsl:call-template name="subjectAnyOrder"/>
+               </subject>
+       </xsl:template>
+       <!-- 1.27 648 tmee-->
+       <xsl:template match="marc:datafield[@tag=648]">
+               <subject>
+                       <xsl:if test="marc:subfield[@code=2]">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code=2]"/>
+                               </xsl:attribute>
+                       </xsl:if>
+                       <xsl:call-template name="uri"/>
+
+                       <xsl:call-template name="subjectAuthority"/>
+                       <temporal>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">abcd</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </temporal>
+                       <xsl:call-template name="subjectAnyOrder"/>
+
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=650]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"/>
+                       <topic>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">abcd</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </topic>
+                       <xsl:call-template name="subjectAnyOrder"/>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=651]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"/>
+                       <xsl:for-each select="marc:subfield[@code='a']">
+                               <geographic>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString" select="."/>
+                                       </xsl:call-template>
+                               </geographic>
+                       </xsl:for-each>
+                       <xsl:call-template name="subjectAnyOrder"/>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=653]">
+               <subject>
+                       <xsl:for-each select="marc:subfield[@code='a']">
+                               <topic>
+                                       <xsl:value-of select="."/>
+                               </topic>
+                       </xsl:for-each>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=656]">
+               <subject>
+                       <xsl:if test="marc:subfield[@code=2]">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code=2]"/>
+                               </xsl:attribute>
+                       </xsl:if>
+                       <occupation>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </occupation>
+               </subject>
+       </xsl:template>
+       <xsl:template name="termsOfAddress">
+               <xsl:if test="marc:subfield[@code='b' or @code='c']">
+                       <namePart type="termsOfAddress">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">bc</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="displayLabel">
+               <xsl:if test="marc:subfield[@code='i']">
+                       <xsl:attribute name="displayLabel">
+                               <xsl:value-of select="marc:subfield[@code='i']"/>
+                       </xsl:attribute>
+               </xsl:if>
+               <xsl:if test="marc:subfield[@code='3']">
+                       <xsl:attribute name="displayLabel">
+                               <xsl:value-of select="marc:subfield[@code='3']"/>
+                       </xsl:attribute>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="isInvalid">
+               <xsl:param name="type"/>
+               <xsl:if
+                       test="marc:subfield[@code='z'] or marc:subfield[@code='y']  or marc:subfield[@code='m']">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:value-of select="$type"/>
+                               </xsl:attribute>
+                               <xsl:attribute name="invalid">
+                                       <xsl:text>yes</xsl:text>
+                               </xsl:attribute>
+                               <xsl:if test="marc:subfield[@code='z']">
+                                       <xsl:value-of select="marc:subfield[@code='z']"/>
+                               </xsl:if>
+                               <xsl:if test="marc:subfield[@code='y']">
+                                       <xsl:value-of select="marc:subfield[@code='y']"/>
+                               </xsl:if>
+                               <xsl:if test="marc:subfield[@code='m']">
+                                       <xsl:value-of select="marc:subfield[@code='m']"/>
+                               </xsl:if>
+                       </identifier>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="subtitle">
+               <xsl:if test="marc:subfield[@code='b']">
+                       <subTitle>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="marc:subfield[@code='b']"/>
+                                               <!--<xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">b</xsl:with-param>                                                                 
+                                               </xsl:call-template>-->
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </subTitle>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="script">
+               <xsl:param name="scriptCode"/>
+               <xsl:attribute name="script">
+                       <xsl:choose>
+                               <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
+                               <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
+                               <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
+                               <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
+                               <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
+                               <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
+                       </xsl:choose>
+               </xsl:attribute>
+       </xsl:template>
+       <xsl:template name="parsePart">
+               <!-- assumes 773$q= 1:2:3<4
+                    with up to 3 levels and one optional start page
+               -->
+               <xsl:variable name="level1">
+                       <xsl:choose>
+                               <xsl:when test="contains(text(),':')">
+                                       <!-- 1:2 -->
+                                       <xsl:value-of select="substring-before(text(),':')"/>
+                               </xsl:when>
+                               <xsl:when test="not(contains(text(),':'))">
+                                       <!-- 1 or 1<3 -->
+                                       <xsl:if test="contains(text(),'&lt;')">
+                                               <!-- 1<3 -->
+                                               <xsl:value-of select="substring-before(text(),'&lt;')"/>
+                                       </xsl:if>
+                                       <xsl:if test="not(contains(text(),'&lt;'))">
+                                               <!-- 1 -->
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:if>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="sici2">
+                       <xsl:choose>
+                               <xsl:when test="starts-with(substring-after(text(),$level1),':')">
+                                       <xsl:value-of select="substring(substring-after(text(),$level1),2)"/>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="substring-after(text(),$level1)"/>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="level2">
+                       <xsl:choose>
+                               <xsl:when test="contains($sici2,':')">
+                                       <!--  2:3<4  -->
+                                       <xsl:value-of select="substring-before($sici2,':')"/>
+                               </xsl:when>
+                               <xsl:when test="contains($sici2,'&lt;')">
+                                       <!-- 1: 2<4 -->
+                                       <xsl:value-of select="substring-before($sici2,'&lt;')"/>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="$sici2"/>
+                                       <!-- 1:2 -->
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="sici3">
+                       <xsl:choose>
+                               <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
+                                       <xsl:value-of select="substring(substring-after($sici2,$level2),2)"/>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="substring-after($sici2,$level2)"/>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="level3">
+                       <xsl:choose>
+                               <xsl:when test="contains($sici3,'&lt;')">
+                                       <!-- 2<4 -->
+                                       <xsl:value-of select="substring-before($sici3,'&lt;')"/>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="$sici3"/>
+                                       <!-- 3 -->
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="page">
+                       <xsl:if test="contains(text(),'&lt;')">
+                               <xsl:value-of select="substring-after(text(),'&lt;')"/>
+                       </xsl:if>
+               </xsl:variable>
+               <xsl:if test="$level1">
+                       <detail level="1">
+                               <number>
+                                       <xsl:value-of select="$level1"/>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$level2">
+                       <detail level="2">
+                               <number>
+                                       <xsl:value-of select="$level2"/>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$level3">
+                       <detail level="3">
+                               <number>
+                                       <xsl:value-of select="$level3"/>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$page">
+                       <extent unit="page">
+                               <start>
+                                       <xsl:value-of select="$page"/>
+                               </start>
+                       </extent>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="getLanguage">
+               <xsl:param name="langString"/>
+               <xsl:param name="controlField008-35-37"/>
+               <xsl:variable name="length" select="string-length($langString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
+                               <xsl:call-template name="getLanguage">
+                                       <xsl:with-param name="langString" select="substring($langString,4,$length)"/>
+                                       <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"/>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <language>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="substring($langString,1,3)"/>
+                                       </languageTerm>
+                               </language>
+                               <xsl:call-template name="getLanguage">
+                                       <xsl:with-param name="langString" select="substring($langString,4,$length)"/>
+                                       <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"/>
+                               </xsl:call-template>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="isoLanguage">
+               <xsl:param name="currentLanguage"/>
+               <xsl:param name="usedLanguages"/>
+               <xsl:param name="remainingLanguages"/>
+               <xsl:choose>
+                       <xsl:when test="string-length($currentLanguage)=0"/>
+                       <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
+                               <language>
+                                       <xsl:if test="@code!='a'">
+                                               <xsl:attribute name="objectPart">
+                                                       <xsl:choose>
+                                                               <xsl:when test="@code='b'">summary or subtitle</xsl:when>
+                                                               <xsl:when test="@code='d'">sung or spoken text</xsl:when>
+                                                               <xsl:when test="@code='e'">libretto</xsl:when>
+                                                               <xsl:when test="@code='f'">table of contents</xsl:when>
+                                                               <xsl:when test="@code='g'">accompanying material</xsl:when>
+                                                               <xsl:when test="@code='h'">translation</xsl:when>
+                                                       </xsl:choose>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="$currentLanguage"/>
+                                       </languageTerm>
+                               </language>
+                               <xsl:call-template name="isoLanguage">
+                                       <xsl:with-param name="currentLanguage">
+                                               <xsl:value-of select="substring($remainingLanguages,1,3)"/>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="usedLanguages">
+                                               <xsl:value-of select="concat($usedLanguages,$currentLanguage)"/>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="remainingLanguages">
+                                               <xsl:value-of
+                                                       select="substring($remainingLanguages,4,string-length($remainingLanguages))"
+                                               />
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <xsl:call-template name="isoLanguage">
+                                       <xsl:with-param name="currentLanguage">
+                                               <xsl:value-of select="substring($remainingLanguages,1,3)"/>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="usedLanguages">
+                                               <xsl:value-of select="concat($usedLanguages,$currentLanguage)"/>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="remainingLanguages">
+                                               <xsl:value-of
+                                                       select="substring($remainingLanguages,4,string-length($remainingLanguages))"
+                                               />
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="chopBrackets">
+               <xsl:param name="chopString"/>
+               <xsl:variable name="string">
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="$chopString"/>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="substring($string, 1,1)='['">
+                       <xsl:value-of select="substring($string,2, string-length($string)-2)"/>
+               </xsl:if>
+               <xsl:if test="substring($string, 1,1)!='['">
+                       <xsl:value-of select="$string"/>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="rfcLanguages">
+               <xsl:param name="nodeNum"/>
+               <xsl:param name="usedLanguages"/>
+               <xsl:param name="controlField008-35-37"/>
+               <xsl:variable name="currentLanguage" select="."/>
+               <xsl:choose>
+                       <xsl:when test="not($currentLanguage)"/>
+                       <xsl:when
+                               test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
+                               <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
+                                       <language>
+                                               <xsl:if test="@code!='a'">
+                                                       <xsl:attribute name="objectPart">
+                                                               <xsl:choose>
+                                                                       <xsl:when test="@code='b'">summary or subtitle</xsl:when>
+                                                                       <xsl:when test="@code='d'">sung or spoken text</xsl:when>
+                                                                       <xsl:when test="@code='e'">libretto</xsl:when>
+                                                                       <xsl:when test="@code='f'">table of contents</xsl:when>
+                                                                       <xsl:when test="@code='g'">accompanying material</xsl:when>
+                                                                       <xsl:when test="@code='h'">translation</xsl:when>
+                                                               </xsl:choose>
+                                                       </xsl:attribute>
+                                               </xsl:if>
+                                               <languageTerm authority="rfc3066" type="code">
+                                                       <xsl:value-of select="$currentLanguage"/>
+                                               </languageTerm>
+                                       </language>
+                               </xsl:if>
+                       </xsl:when>
+                       <xsl:otherwise> </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+
+    <xsl:template name="datafield">
+               <xsl:param name="tag"/>
+               <xsl:param name="ind1">
+                       <xsl:text> </xsl:text>
+               </xsl:param>
+               <xsl:param name="ind2">
+                       <xsl:text> </xsl:text>
+               </xsl:param>
+               <xsl:param name="subfields"/>
+               <xsl:element name="marc:datafield">
+                       <xsl:attribute name="tag">
+                               <xsl:value-of select="$tag"/>
+                       </xsl:attribute>
+                       <xsl:attribute name="ind1">
+                               <xsl:value-of select="$ind1"/>
+                       </xsl:attribute>
+                       <xsl:attribute name="ind2">
+                               <xsl:value-of select="$ind2"/>
+                       </xsl:attribute>
+                       <xsl:copy-of select="$subfields"/>
+               </xsl:element>
+       </xsl:template>
+
+       <xsl:template name="subfieldSelect">
+               <xsl:param name="codes">abcdefghijklmnopqrstuvwxyz</xsl:param>
+               <xsl:param name="delimeter">
+                       <xsl:text> </xsl:text>
+               </xsl:param>
+               <xsl:variable name="str">
+                       <xsl:for-each select="marc:subfield">
+                               <xsl:if test="contains($codes, @code)">
+                                       <xsl:value-of select="text()"/>
+                                       <xsl:value-of select="$delimeter"/>
+                               </xsl:if>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
+       </xsl:template>
+
+       <xsl:template name="buildSpaces">
+               <xsl:param name="spaces"/>
+               <xsl:param name="char">
+                       <xsl:text> </xsl:text>
+               </xsl:param>
+               <xsl:if test="$spaces>0">
+                       <xsl:value-of select="$char"/>
+                       <xsl:call-template name="buildSpaces">
+                               <xsl:with-param name="spaces" select="$spaces - 1"/>
+                               <xsl:with-param name="char" select="$char"/>
+                       </xsl:call-template>
+               </xsl:if>
+       </xsl:template>
+
+       <xsl:template name="chopPunctuation">
+               <xsl:param name="chopString"/>
+               <xsl:param name="punctuation">
+                       <xsl:text>.:,;/ </xsl:text>
+               </xsl:param>
+               <xsl:variable name="length" select="string-length($chopString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
+                                       <xsl:with-param name="punctuation" select="$punctuation"/>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:when test="not($chopString)"/>
+                       <xsl:otherwise>
+                               <xsl:value-of select="$chopString"/>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+
+       <xsl:template name="chopPunctuationFront">
+               <xsl:param name="chopString"/>
+               <xsl:variable name="length" select="string-length($chopString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
+                               <xsl:call-template name="chopPunctuationFront">
+                                       <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"
+                                       />
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:when test="not($chopString)"/>
+                       <xsl:otherwise>
+                               <xsl:value-of select="$chopString"/>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+
+       <xsl:template name="chopPunctuationBack">
+               <xsl:param name="chopString"/>
+               <xsl:param name="punctuation">
+                       <xsl:text>.:,;/] </xsl:text>
+               </xsl:param>
+               <xsl:variable name="length" select="string-length($chopString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
+                                       <xsl:with-param name="punctuation" select="$punctuation"/>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:when test="not($chopString)"/>
+                       <xsl:otherwise>
+                               <xsl:value-of select="$chopString"/>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+
+       <!-- nate added 12/14/2007 for lccn.loc.gov: url encode ampersand, etc. -->
+       <xsl:template name="url-encode">
+
+               <xsl:param name="str"/>
+
+               <xsl:if test="$str">
+                       <xsl:variable name="first-char" select="substring($str,1,1)"/>
+                       <xsl:choose>
+                               <xsl:when test="contains($safe,$first-char)">
+                                       <xsl:value-of select="$first-char"/>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:variable name="codepoint">
+                                               <xsl:choose>
+                                                       <xsl:when test="contains($ascii,$first-char)">
+                                                               <xsl:value-of
+                                                                       select="string-length(substring-before($ascii,$first-char)) + 32"
+                                                               />
+                                                       </xsl:when>
+                                                       <xsl:when test="contains($latin1,$first-char)">
+                                                               <xsl:value-of
+                                                                       select="string-length(substring-before($latin1,$first-char)) + 160"/>
+                                                               <!-- was 160 -->
+                                                       </xsl:when>
+                                                       <xsl:otherwise>
+                                                               <xsl:message terminate="no">Warning: string contains a character
+                                                                       that is out of range! Substituting "?".</xsl:message>
+                                                               <xsl:text>63</xsl:text>
+                                                       </xsl:otherwise>
+                                               </xsl:choose>
+                                       </xsl:variable>
+                                       <xsl:variable name="hex-digit1"
+                                               select="substring($hex,floor($codepoint div 16) + 1,1)"/>
+                                       <xsl:variable name="hex-digit2" select="substring($hex,$codepoint mod 16 + 1,1)"/>
+                                       <!-- <xsl:value-of select="concat('%',$hex-digit2)"/> -->
+                                       <xsl:value-of select="concat('%',$hex-digit1,$hex-digit2)"/>
+                               </xsl:otherwise>
+                       </xsl:choose>
+                       <xsl:if test="string-length($str) &gt; 1">
+                               <xsl:call-template name="url-encode">
+                                       <xsl:with-param name="str" select="substring($str,2)"/>
+                               </xsl:call-template>
+                       </xsl:if>
+               </xsl:if>
+       </xsl:template>
+</xsl:stylesheet>$$ WHERE name = 'mods33';
+
+
+INSERT INTO config.global_flag (name, value, enabled, label) VALUES
+(
+    'opac.browse.warnable_regexp_per_class',
+    '{"title": "^(a|the|an)\\s"}',
+    FALSE,
+    oils_i18n_gettext(
+        'opac.browse.warnable_regexp_per_class',
+        'Map of search classes to regular expressions to warn user about leading articles.',
+        'cgf',
+        'label'
+    )
+),
+(
+    'opac.browse.holdings_visibility_test_limit',
+    '100',
+    TRUE,
+    oils_i18n_gettext(
+        'opac.browse.holdings_visibility_test_limit',
+        'Don''t look for more than this number of records with holdings when displaying browse headings with visible record counts.',
+        'cgf',
+        'label'
+    )
+);
+
+ALTER TABLE metabib.browse_entry DROP CONSTRAINT browse_entry_value_key;
+ALTER TABLE metabib.browse_entry ADD COLUMN sort_value TEXT;
+DELETE FROM metabib.browse_entry_def_map; -- Yeah.
+DELETE FROM metabib.browse_entry WHERE sort_value IS NULL;
+ALTER TABLE metabib.browse_entry ALTER COLUMN sort_value SET NOT NULL;
+ALTER TABLE metabib.browse_entry ADD UNIQUE (sort_value, value);
+DROP TRIGGER IF EXISTS mbe_sort_value ON metabib.browse_entry;
+
+CREATE INDEX browse_entry_sort_value_idx
+    ON metabib.browse_entry USING BTREE (sort_value);
+
+-- NOTE If I understand ordered indices correctly, an index on sort_value DESC
+-- is not actually needed, even though we do have a query that does ORDER BY
+-- on this column in that direction.  The previous index serves for both
+-- directions, and ordering in an index is only helpful for multi-column
+-- indices, I think. See http://www.postgresql.org/docs/9.1/static/indexes-ordering.html
+
+-- CREATE INDEX CONCURRENTLY browse_entry_sort_value_idx_desc
+--     ON metabib.browse_entry USING BTREE (sort_value DESC);
+
+CREATE TYPE metabib.flat_browse_entry_appearance AS (
+    browse_entry    BIGINT,
+    value           TEXT,
+    fields          TEXT,
+    authorities     TEXT,
+    sources         INT,        -- visible ones, that is
+    row_number      INT,        -- internal use, sort of
+    accurate        BOOL,       -- Count in sources field is accurate? Not
+                                -- if we had more than a browse superpage
+                                -- of records to look at.
+    pivot_point     BIGINT
+);
+
+
+CREATE OR REPLACE FUNCTION metabib.browse_pivot(
+    search_field        INT[],
+    browse_term         TEXT
+) RETURNS BIGINT AS $p$
+DECLARE
+    id                  BIGINT;
+BEGIN
+    SELECT INTO id mbe.id FROM metabib.browse_entry mbe
+        JOIN metabib.browse_entry_def_map mbedm ON (
+            mbedm.entry = mbe.id AND
+            mbedm.def = ANY(search_field)
+        )
+        WHERE mbe.sort_value >= public.search_normalize(browse_term)
+        ORDER BY mbe.sort_value, mbe.value LIMIT 1;
+
+    RETURN id;
+END;
+$p$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION metabib.staged_browse(
+    query                   TEXT,
+    fields                  INT[],
+    context_org             INT,
+    context_locations       INT[],
+    staff                   BOOL,
+    browse_superpage_size   INT,
+    count_up_from_zero      BOOL,   -- if false, count down from -1
+    result_limit            INT,
+    next_pivot_pos          INT
+) RETURNS SETOF metabib.flat_browse_entry_appearance AS $p$
+DECLARE
+    curs                    REFCURSOR;
+    rec                     RECORD;
+    qpfts_query             TEXT;
+    result_row              metabib.flat_browse_entry_appearance%ROWTYPE;
+    results_skipped         INT := 0;
+    row_counter             INT := 0;
+    row_number              INT;
+    slice_start             INT;
+    slice_end               INT;
+    full_end                INT;
+    all_records             BIGINT[];
+    superpage_of_records    BIGINT[];
+    superpage_size          INT;
+BEGIN
+    IF count_up_from_zero THEN
+        row_number := 0;
+    ELSE
+        row_number := -1;
+    END IF;
+
+    OPEN curs FOR EXECUTE query;
+
+    LOOP
+        FETCH curs INTO rec;
+        IF NOT FOUND THEN
+            IF result_row.pivot_point IS NOT NULL THEN
+                RETURN NEXT result_row;
+            END IF;
+            RETURN;
+        END IF;
+
+        -- Gather aggregate data based on the MBE row we're looking at now
+        SELECT INTO all_records, result_row.authorities, result_row.fields
+                ARRAY_AGG(DISTINCT source),
+                ARRAY_TO_STRING(ARRAY_AGG(DISTINCT authority), $$,$$),
+                ARRAY_TO_STRING(ARRAY_AGG(DISTINCT def), $$,$$)
+          FROM  metabib.browse_entry_def_map
+          WHERE entry = rec.id
+                AND def = ANY(fields);
+
+        result_row.sources := 0;
+
+        full_end := ARRAY_LENGTH(all_records, 1);
+        superpage_size := COALESCE(browse_superpage_size, full_end);
+        slice_start := 1;
+        slice_end := superpage_size;
+
+        WHILE result_row.sources = 0 AND slice_start <= full_end LOOP
+            superpage_of_records := all_records[slice_start:slice_end];
+            qpfts_query :=
+                'SELECT NULL::BIGINT AS id, ARRAY[r] AS records, ' ||
+                '1::INT AS rel FROM (SELECT UNNEST(' ||
+                quote_literal(superpage_of_records) || '::BIGINT[]) AS r) rr';
+
+            -- We use search.query_parser_fts() for visibility testing.
+            -- We're calling it once per browse-superpage worth of records
+            -- out of the set of records related to a given mbe, until we've
+            -- either exhausted that set of records or found at least 1
+            -- visible record.
+
+            SELECT INTO result_row.sources visible
+                FROM search.query_parser_fts(
+                    context_org, NULL, qpfts_query, NULL,
+                    context_locations, 0, NULL, NULL, FALSE, staff, FALSE
+                ) qpfts
+                WHERE qpfts.rel IS NULL;
+
+            slice_start := slice_start + superpage_size;
+            slice_end := slice_end + superpage_size;
+        END LOOP;
+
+        -- Accurate?  Well, probably.
+        result_row.accurate := browse_superpage_size IS NULL OR
+            browse_superpage_size >= full_end;
+
+        IF result_row.sources > 0 THEN
+            -- We've got a browse entry with visible holdings. Yay.
+
+
+            -- The function that calls this function needs row_number in order
+            -- to correctly order results from two different runs of this
+            -- functions.
+            result_row.row_number := row_number;
+
+            -- Now, if row_counter is still less than limit, return a row.  If
+            -- not, but it is less than next_pivot_pos, continue on without
+            -- returning actual result rows until we find
+            -- that next pivot, and return it.
+
+            IF row_counter < result_limit THEN
+                result_row.browse_entry := rec.id;
+                result_row.value := rec.value;
+
+                RETURN NEXT result_row;
+            ELSE
+                result_row.browse_entry := NULL;
+                result_row.authorities := NULL;
+                result_row.fields := NULL;
+                result_row.value := NULL;
+                result_row.sources := NULL;
+                result_row.accurate := NULL;
+                result_row.pivot_point := rec.id;
+
+                IF row_counter >= next_pivot_pos THEN
+                    RETURN NEXT result_row;
+                    RETURN;
+                END IF;
+            END IF;
+
+            IF count_up_from_zero THEN
+                row_number := row_number + 1;
+            ELSE
+                row_number := row_number - 1;
+            END IF;
+
+            -- row_counter is different from row_number.
+            -- It simply counts up from zero so that we know when
+            -- we've reached our limit.
+            row_counter := row_counter + 1;
+        END IF;
+    END LOOP;
+END;
+$p$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION metabib.browse(
+    search_field            INT[],
+    browse_term             TEXT,
+    context_org             INT DEFAULT NULL,
+    context_loc_group       INT DEFAULT NULL,
+    staff                   BOOL DEFAULT FALSE,
+    pivot_id                BIGINT DEFAULT NULL,
+    result_limit            INT DEFAULT 10
+) RETURNS SETOF metabib.flat_browse_entry_appearance AS $p$
+DECLARE
+    core_query              TEXT;
+    back_query              TEXT;
+    forward_query           TEXT;
+    pivot_sort_value        TEXT;
+    pivot_sort_fallback     TEXT;
+    context_locations       INT[];
+    browse_superpage_size   INT;
+    results_skipped         INT := 0;
+    back_limit              INT;
+    back_to_pivot           INT;
+    forward_limit           INT;
+    forward_to_pivot        INT;
+BEGIN
+    -- First, find the pivot if we were given a browse term but not a pivot.
+    IF pivot_id IS NULL THEN
+        pivot_id := metabib.browse_pivot(search_field, browse_term);
+    END IF;
+
+    SELECT INTO pivot_sort_value, pivot_sort_fallback
+        sort_value, value FROM metabib.browse_entry WHERE id = pivot_id;
+
+    -- Bail if we couldn't find a pivot.
+    IF pivot_sort_value IS NULL THEN
+        RETURN;
+    END IF;
+
+    -- Transform the context_loc_group argument (if any) (logc at the
+    -- TPAC layer) into a form we'll be able to use.
+    IF context_loc_group IS NOT NULL THEN
+        SELECT INTO context_locations ARRAY_AGG(location)
+            FROM asset.copy_location_group_map
+            WHERE lgroup = context_loc_group;
+    END IF;
+
+    -- Get the configured size of browse superpages.
+    SELECT INTO browse_superpage_size value     -- NULL ok
+        FROM config.global_flag
+        WHERE enabled AND name = 'opac.browse.holdings_visibility_test_limit';
+
+    -- First we're going to search backward from the pivot, then we're going
+    -- to search forward.  In each direction, we need two limits.  At the
+    -- lesser of the two limits, we delineate the edge of the result set
+    -- we're going to return.  At the greater of the two limits, we find the
+    -- pivot value that would represent an offset from the current pivot
+    -- at a distance of one "page" in either direction, where a "page" is a
+    -- result set of the size specified in the "result_limit" argument.
+    --
+    -- The two limits in each direction make four derived values in total,
+    -- and we calculate them now.
+    back_limit := CEIL(result_limit::FLOAT / 2);
+    back_to_pivot := result_limit;
+    forward_limit := result_limit / 2;
+    forward_to_pivot := result_limit - 1;
+
+    -- This is the meat of the SQL query that finds browse entries.  We'll
+    -- pass this to a function which uses it with a cursor, so that individual
+    -- rows may be fetched in a loop until some condition is satisfied, without
+    -- waiting for a result set of fixed size to be collected all at once.
+    core_query := '
+    SELECT
+        mbe.id,
+        mbe.value,
+        mbe.sort_value
+    FROM metabib.browse_entry mbe
+    WHERE EXISTS (SELECT 1 FROM  metabib.browse_entry_def_map mbedm WHERE
+        mbedm.entry = mbe.id AND
+        mbedm.def = ANY(' || quote_literal(search_field) || ')
+    ) AND ';
+
+    -- This is the variant of the query for browsing backward.
+    back_query := core_query ||
+        ' mbe.sort_value <= ' || quote_literal(pivot_sort_value) ||
+    ' ORDER BY mbe.sort_value DESC, mbe.value DESC ';
+
+    -- This variant browses forward.
+    forward_query := core_query ||
+        ' mbe.sort_value > ' || quote_literal(pivot_sort_value) ||
+    ' ORDER BY mbe.sort_value, mbe.value ';
+
+    -- We now call the function which applies a cursor to the provided
+    -- queries, stopping at the appropriate limits and also giving us
+    -- the next page's pivot.
+    RETURN QUERY
+        SELECT * FROM metabib.staged_browse(
+            back_query, search_field, context_org, context_locations,
+            staff, browse_superpage_size, TRUE, back_limit, back_to_pivot
+        ) UNION
+        SELECT * FROM metabib.staged_browse(
+            forward_query, search_field, context_org, context_locations,
+            staff, browse_superpage_size, FALSE, forward_limit, forward_to_pivot
+        ) ORDER BY row_number DESC;
+
+END;
+$p$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION metabib.browse(
+    search_class        TEXT,
+    browse_term         TEXT,
+    context_org         INT DEFAULT NULL,
+    context_loc_group   INT DEFAULT NULL,
+    staff               BOOL DEFAULT FALSE,
+    pivot_id            BIGINT DEFAULT NULL,
+    result_limit        INT DEFAULT 10
+) RETURNS SETOF metabib.flat_browse_entry_appearance AS $p$
+BEGIN
+    RETURN QUERY SELECT * FROM metabib.browse(
+        (SELECT COALESCE(ARRAY_AGG(id), ARRAY[]::INT[])
+            FROM config.metabib_field WHERE field_class = search_class),
+        browse_term,
+        context_org,
+        context_loc_group,
+        staff,
+        pivot_id,
+        result_limit
+    );
+END;
+$p$ LANGUAGE PLPGSQL;
+
+UPDATE config.metabib_field
+SET
+    xpath = $$//mods32:mods/mods32:relatedItem[@type="series"]/mods32:titleInfo[@type="nfi"]$$,
+    browse_sort_xpath = $$*[local-name() != "nonSort"]$$,
+    browse_xpath = NULL
+WHERE
+    field_class = 'series' AND name = 'seriestitle' ;
+
+UPDATE config.metabib_field
+SET
+    xpath = $$//mods32:mods/mods32:titleInfo[mods32:title and not (@type)]$$,
+    browse_sort_xpath = $$*[local-name() != "nonSort"]$$,
+    browse_xpath = NULL,
+    browse_field = TRUE
+WHERE
+    field_class = 'title' AND name = 'proper' ;
+
+UPDATE config.metabib_field
+SET
+    xpath = $$//mods32:mods/mods32:titleInfo[mods32:title and (@type='alternative-nfi')]$$,
+    browse_sort_xpath = $$*[local-name() != "nonSort"]$$,
+    browse_xpath = NULL
+WHERE
+    field_class = 'title' AND name = 'alternative' ;
+
+UPDATE config.metabib_field
+SET
+    xpath = $$//mods32:mods/mods32:titleInfo[mods32:title and (@type='uniform-nfi')]$$,
+    browse_sort_xpath = $$*[local-name() != "nonSort"]$$,
+    browse_xpath = NULL
+WHERE
+    field_class = 'title' AND name = 'uniform' ;
+
+UPDATE config.metabib_field
+SET
+    xpath = $$//mods32:mods/mods32:titleInfo[mods32:title and (@type='translated-nfi')]$$,
+    browse_sort_xpath = $$*[local-name() != "nonSort"]$$,
+    browse_xpath = NULL
+WHERE
+    field_class = 'title' AND name = 'translated' ;
+
+-- This keeps extra terms like "creator" out of browse headings.
+UPDATE config.metabib_field
+    SET browse_xpath = $$//*[local-name()='namePart']$$     -- vim */
+    WHERE
+        browse_field AND
+        browse_xpath IS NULL AND
+        field_class = 'author';
+
+INSERT INTO config.org_unit_setting_type (
+    name, label, grp, description, datatype
+) VALUES (
+    'opac.browse.pager_shortcuts',
+    'Paging shortcut links for OPAC Browse',
+    'opac',
+    'The characters in this string, in order, will be used as shortcut links for quick paging in the OPAC browse interface. Any sequence surrounded by asterisks will be taken as a whole label, not split into individual labels at the character level, but only the first character will serve as the basis of the search.',
+    'string'
+);
+
+-- NOTE: very IDs are still correct for perms and event_def data at merge.
+
+SELECT evergreen.upgrade_deps_block_check('0817', :eg_version);
+
+-- copy status
+
+INSERT INTO config.copy_status
+    (id, name, holdable, opac_visible, copy_active, restrict_copy_delete)
+    VALUES (16, oils_i18n_gettext(16, 'Long Overdue', 'ccs', 'name'), 'f', 'f', 'f', 't');
+
+-- checkin override perm
+
+INSERT INTO permission.perm_list (id, code, description) VALUES (
+    549, -- VERIFY
+    'COPY_STATUS_LONGOVERDUE.override',
+    oils_i18n_gettext(
+        549, -- VERIFY
+        'Allows the user to check-in long-overdue items, prompting ' ||
+            'long-overdue check-in processing',
+        'ppl',
+        'code'
+    )
+), (
+    550, -- VERIFY
+    'SET_CIRC_LONG_OVERDUE',
+    oils_i18n_gettext(
+        550, -- VERIFY
+        'Allows the user to mark a circulation as long-overdue',
+        'ppl',
+        'code'
+    )
+);
+
+-- billing types
+
+INSERT INTO config.billing_type (id, owner, name) VALUES
+    (10, 1, oils_i18n_gettext(
+        10, 'Long-Overdue Materials', 'cbt', 'name')),
+    (11, 1, oils_i18n_gettext(
+        11, 'Long-Overdue Materials Processing Fee', 'cbt', 'name'));
+
+-- org settings
+
+INSERT INTO config.org_unit_setting_type 
+    (name, grp, datatype, label, description) VALUES 
+(
+    'circ.longoverdue_immediately_available',
+    'circ', 'bool',
+    oils_i18n_gettext(
+        'circ.longoverdue_immediately_available',
+        'Long-Overdue Items Usable on Checkin',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.longoverdue_immediately_available',
+        'Long-overdue items are usable on checkin instead of going "home" first',
+        'coust',
+        'description'
+    )
+), (
+    'circ.longoverdue_materials_processing_fee',
+    'finance', 'currency',
+    oils_i18n_gettext(
+        'circ.longoverdue_materials_processing_fee',
+        'Long-Overdue Materials Processing Fee',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.longoverdue_materials_processing_fee',
+        'Long-Overdue Materials Processing Fee',
+        'coust',
+        'description'
+    )
+), (
+    'circ.max_accept_return_of_longoverdue',
+    'circ', 'interval',
+    oils_i18n_gettext(
+        'circ.max_accept_return_of_longoverdue',
+        'Long-Overdue Max Return Interval',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.max_accept_return_of_longoverdue',
+        'Long-overdue check-in processing (voiding fees, re-instating ' ||
+            'overdues, etc.) will not take place for items that have been ' ||
+            'overdue for (or have last activity older than) this amount of time',
+        'coust',
+        'description'
+    )
+), (
+    'circ.restore_overdue_on_longoverdue_return',
+    'circ', 'bool',
+    oils_i18n_gettext(
+        'circ.restore_overdue_on_longoverdue_return',
+        'Restore Overdues on Long-Overdue Item Return',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.restore_overdue_on_longoverdue_return',
+        'Restore Overdues on Long-Overdue Item Return',
+        'coust',
+        'description'
+    )
+), (
+    'circ.void_longoverdue_on_checkin',
+    'circ', 'bool',
+    oils_i18n_gettext(
+        'circ.void_longoverdue_on_checkin',
+        'Void Long-Overdue Item Billing When Returned',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.void_longoverdue_on_checkin',
+        'Void Long-Overdue Item Billing When Returned',
+        'coust',
+        'description'
+    )
+), (
+    'circ.void_longoverdue_proc_fee_on_checkin',
+    'circ', 'bool',
+    oils_i18n_gettext(
+        'circ.void_longoverdue_proc_fee_on_checkin',
+        'Void Processing Fee on Long-Overdue Item Return',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.void_longoverdue_proc_fee_on_checkin',
+        'Void Processing Fee on Long-Overdue Item Return',
+        'coust',
+        'description'
+    )
+), (
+    'circ.void_overdue_on_longoverdue',
+    'finance', 'bool',
+    oils_i18n_gettext(
+        'circ.void_overdue_on_longoverdue',
+        'Void Overdue Fines When Items are Marked Long-Overdue',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.void_overdue_on_longoverdue',
+        'Void Overdue Fines When Items are Marked Long-Overdue',
+        'coust',
+        'description'
+    )
+), (
+    'circ.longoverdue.xact_open_on_zero',
+    'finance', 'bool',
+    oils_i18n_gettext(
+        'circ.longoverdue.xact_open_on_zero',
+        'Leave transaction open when long overdue balance equals zero',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.longoverdue.xact_open_on_zero',
+        'Leave transaction open when long-overdue balance equals zero.  ' ||
+            'This leaves the lost copy on the patron record when it is paid',
+        'coust',
+        'description'
+    )
+), (
+    'circ.longoverdue.use_last_activity_date_on_return',
+    'circ', 'bool',
+    oils_i18n_gettext(
+        'circ.longoverdue.use_last_activity_date_on_return',
+        'Long-Overdue Check-In Interval Uses Last Activity Date',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.longoverdue.use_last_activity_date_on_return',
+        'Use the long-overdue last-activity date instead of the due_date to ' ||
+            'determine whether the item has been checked out too long to ' ||
+            'perform long-overdue check-in processing.  If set, the system ' ||
+            'will first check the last payment time, followed by the last ' ||
+            'billing time, followed by the due date.  See also ' ||
+            'circ.max_accept_return_of_longoverdue',
+        'coust',
+        'description'
+    )
+);
+
+-- mark long-overdue reactor
+
+INSERT INTO action_trigger.reactor (module, description) VALUES
+(   'MarkItemLongOverdue',
+    oils_i18n_gettext(
+        'MarkItemLongOverdue',
+        'Marks a circulating item as long-overdue and applies configured ' ||
+        'penalties.  Also creates events for the longoverdue.auto hook',
+        'atreact',
+        'description'
+    )
+);
+
+INSERT INTO action_trigger.validator (module, description) VALUES (
+    'PatronNotInCollections', 
+    'Event is valid if the linked patron is not in collections processing ' ||
+        'at the context org unit'
+);
+
+-- VERIFY ID
+INSERT INTO action_trigger.event_definition 
+    (id, active, owner, name, hook, validator, reactor, delay, delay_field) 
+VALUES ( 
+    49, FALSE, 1, '6 Month Overdue Mark Long-Overdue', 
+    'checkout.due', 'PatronNotInCollections', 
+    'MarkItemLongOverdue', '6 months', 'due_date'
+);
+
+-- VERIFY ID
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+    (49, 'editor', '''1'''); 
+
+-- new longoverdue and longervdue.auto hook.
+
+INSERT INTO action_trigger.hook (key,core_type,description) VALUES (
+    'longoverdue',
+    'circ',
+    'Circulating Item marked long-overdue'
+);
+
+INSERT INTO action_trigger.hook (key,core_type,description) VALUES (
+    'longoverdue.auto',
+    'circ',
+    'Circulating Item automatically marked long-overdue'
+);
+
+-- sample longoverdue.auto notification reactor
+
+-- VERIFY ID
+INSERT INTO action_trigger.event_definition 
+    (id, active, owner, name, hook, validator, reactor, group_field, template) 
+    VALUES ( 
+        50, FALSE, 1, '6 Month Long Overdue Notice', 
+        'longoverdue.auto', 'NOOP_True', 'SendEmail', 'usr',
+$$
+[%- USE date -%]
+[%- user = target.0.usr -%]
+To: [%- params.recipient_email || user.email %]
+From: [%- params.sender_email || default_sender %]
+Subject: Overdue Items Marked Long Overdue
+
+Dear [% user.family_name %], [% user.first_given_name %]
+The following items are 6 months overdue and have been marked Long Overdue.
+
+[% FOR circ IN target %]
+    [%- copy_details = helpers.get_copy_bib_basics(circ.target_copy.id) -%]
+    Title: [% copy_details.title %], by [% copy_details.author %]
+    Call Number: [% circ.target_copy.call_number.label %]
+    Shelving Location: [% circ.target_copy.location.name %]
+    Barcode: [% circ.target_copy.barcode %]
+    Due: [% date.format(helpers.format_date(circ.due_date), '%Y-%m-%d') %]
+    Item Cost: [% helpers.get_copy_price(circ.target_copy) %]
+    Total Owed For Transaction: [% circ.billable_transaction.summary.balance_owed %]
+    Library: [% circ.circ_lib.name %]
+
+[% END %]
+$$);
+
+-- ENV for above
+
+-- VERIFY IDs
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+    (50, 'target_copy.call_number'),
+    (50, 'usr'),
+    (50, 'billable_transaction.summary'),
+    (50, 'circ_lib.billing_address'),
+    (50, 'target_copy.location');
+
+
+--ROLLBACK;
+
+SELECT evergreen.upgrade_deps_block_check('0818', :eg_version);
+
+INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype ) VALUES (
+    'circ.patron_edit.duplicate_patron_check_depth', 'circ',
+    oils_i18n_gettext(
+        'circ.patron_edit.duplicate_patron_check_depth',
+        'Specify search depth for the duplicate patron check in the patron editor',
+        'coust',
+        'label'),
+    oils_i18n_gettext(
+        'circ.patron_edit.duplicate_patron_check_depth',
+        'When using the patron registration page, the duplicate patron check will use the configured depth to scope the search for duplicate patrons.',
+        'coust',
+        'description'),
+    'integer')
+;
+
+
+
+-- Evergreen DB patch 0819.schema.acn_dewey_normalizer.sql
+--
+-- Fixes Dewey call number sorting (per LP# 1150939)
+--
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0819', :eg_version);
+
+CREATE OR REPLACE FUNCTION asset.label_normalizer_dewey(TEXT) RETURNS TEXT AS $func$
+    # Derived from the Koha C4::ClassSortRoutine::Dewey module
+    # Copyright (C) 2007 LibLime
+    # Licensed under the GPL v2 or later
+
+    use strict;
+    use warnings;
+
+    my $init = uc(shift);
+    $init =~ s/^\s+//;
+    $init =~ s/\s+$//;
+    $init =~ s!/!!g;
+    $init =~ s/^([\p{IsAlpha}]+)/$1 /;
+    my @tokens = split /\.|\s+/, $init;
+    my $digit_group_count = 0;
+    my $first_digit_group_idx;
+    for (my $i = 0; $i <= $#tokens; $i++) {
+        if ($tokens[$i] =~ /^\d+$/) {
+            $digit_group_count++;
+            if ($digit_group_count == 1) {
+                $first_digit_group_idx = $i;
+            }
+            if (2 == $digit_group_count) {
+                $tokens[$i] = sprintf("%-15.15s", $tokens[$i]);
+                $tokens[$i] =~ tr/ /0/;
+            }
+        }
+    }
+    # Pad the first digit_group if there was only one
+    if (1 == $digit_group_count) {
+        $tokens[$first_digit_group_idx] .= '_000000000000000'
+    }
+    my $key = join("_", @tokens);
+    $key =~ s/[^\p{IsAlnum}_]//g;
+
+    return $key;
+
+$func$ LANGUAGE PLPERLU;
+
+-- Remove [ and ] characters from seriestitle.
+-- Those characters don't play well when searching.
+
+SELECT evergreen.upgrade_deps_block_check('0820', :eg_version); -- Callender
+
+INSERT INTO config.metabib_field_index_norm_map (field,norm,params, pos)
+     SELECT  m.id,
+             i.id,
+             $$["]",""]$$,
+             '-1'
+       FROM  config.metabib_field m,
+             config.index_normalizer i
+       WHERE i.func IN ('replace')
+             AND m.id IN (1);
+             
+INSERT INTO config.metabib_field_index_norm_map (field,norm,params, pos)
+     SELECT  m.id,
+             i.id,
+             $$["[",""]$$,
+             '-1'
+       FROM  config.metabib_field m,
+             config.index_normalizer i
+       WHERE i.func IN ('replace')
+             AND m.id IN (1);
+
+SELECT evergreen.upgrade_deps_block_check('0821', :eg_version);
+
+-- Placeholder script for 0821 which was backported for fixing 2.3 and 2.4 
+-- only, and not master.
+
+-- Nothing to do here.
+
+SELECT evergreen.upgrade_deps_block_check('0822', :eg_version);
+
+ALTER TABLE action.hold_request 
+    ADD COLUMN behind_desk BOOLEAN NOT NULL DEFAULT FALSE;
+
+-- The value on the hold is the new arbiter of whether a 
+-- hold should be held behind the desk and reported as such
+-- Update existing holds that would in the current regime
+-- be considered behind-the-desk holds to use the new column
+
+UPDATE action.hold_request ahr
+    SET behind_desk = TRUE
+    FROM actor.usr_setting aus
+    WHERE 
+        ahr.cancel_time IS NULL AND
+        ahr.fulfillment_time IS NULL AND
+        aus.usr = ahr.usr AND
+        aus.name = 'circ.holds_behind_desk' AND
+        aus.value = 'true' AND
+        EXISTS (
+            SELECT 1 
+            FROM actor.org_unit_ancestor_setting(
+                'circ.holds.behind_desk_pickup_supported', 
+                ahr.pickup_lib
+            ) 
+            WHERE value = 'true'
+        );
+
+SELECT evergreen.upgrade_deps_block_check('0823', :eg_version);
+
+-- Track the requesting user
+ALTER TABLE staging.user_stage
+    ADD COLUMN requesting_usr INTEGER 
+        REFERENCES actor.usr(id) ON DELETE SET NULL;
+
+-- add county column to staged address tables and 
+-- drop state requirement to match actor.usr_address
+ALTER TABLE staging.mailing_address_stage 
+    ADD COLUMN county TEXT,
+    ALTER COLUMN state DROP DEFAULT,
+    ALTER COLUMN state DROP NOT NULL;
+
+ALTER TABLE staging.billing_address_stage 
+    ADD COLUMN county TEXT,
+    ALTER COLUMN state DROP DEFAULT,
+    ALTER COLUMN state DROP NOT NULL;
+
+-- stored procedure for deleting expired pending patrons
+CREATE OR REPLACE FUNCTION staging.purge_pending_users() RETURNS VOID AS $$
+DECLARE
+    org_id INT;
+    intvl TEXT;
+BEGIN
+    FOR org_id IN SELECT DISTINCT(home_ou) FROM staging.user_stage LOOP
+
+        SELECT INTO intvl value FROM 
+            actor.org_unit_ancestor_setting(
+                'opac.pending_user_expire_interval', org_id);
+
+        CONTINUE WHEN intvl IS NULL OR intvl ILIKE 'null';
+
+        -- de-JSON-ify the string
+        SELECT INTO intvl TRIM(BOTH '"' FROM intvl);
+
+        DELETE FROM staging.user_stage 
+            WHERE home_ou = org_id AND row_date + intvl::INTERVAL < NOW();
+
+    END LOOP;
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, datatype, label, description)
+VALUES (
+    'opac.allow_pending_user',
+    'opac',
+    'bool',
+    oils_i18n_gettext(
+        'opac.allow_pending_user',
+        'Allow Patron Self-Registration',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'opac.allow_pending_user',
+        'Allow patrons to self-register, creating pending user accounts',
+        'coust',
+        'description'
+    )
+), (
+    'opac.pending_user_expire_interval',
+    'opac',
+    'interval',
+    oils_i18n_gettext(
+        'opac.pending_user_expire_interval',
+        'Patron Self-Reg. Expire Interval',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'opac.pending_user_expire_interval',
+        'If set, this is the amount of time a pending user account will ' ||
+        'be allowed to sit in the database.  After this time, the pending ' ||
+        'user information will be purged',
+        'coust',
+        'description'
+    )
+), (
+    'ui.patron.edit.aua.county.show',
+    'gui',
+    'bool',
+    oils_i18n_gettext(
+        'ui.patron.edit.aua.county.require',
+        'Show county field on patron registration',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ui.patron.edit.aua.county.require',
+        'The county field will be shown on the patron registration screen',
+        'coust',
+        'description'
+    )
+);
+
+SELECT evergreen.upgrade_deps_block_check('0824', :eg_version);
+
+INSERT INTO config.org_unit_setting_type
+    (grp, name, label, description, datatype, fm_class)
+VALUES (
+    'vandelay',
+    'vandelay.item.barcode.auto',
+    oils_i18n_gettext(
+        'vandelay.item.barcode.auto',
+        'Vandelay Generate Default Barcodes',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'vandelay.item.barcode.auto',
+        'Auto-generate deault item barcodes when no item barcode is present',
+        'coust', 'label'),
+    'bool',
+    NULL
+), (
+    'vandelay',
+    'vandelay.item.barcode.prefix',
+    oils_i18n_gettext(
+        'vandelay.item.barcode.prefix',
+        'Vandelay Default Barcode Prefix',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'vandelay.item.barcode.prefix',
+        'Apply this prefix to any auto-generated item barcodes',
+        'coust', 'label'),
+    'string',
+    NULL
+), (
+    'vandelay',
+    'vandelay.item.call_number.auto',
+    oils_i18n_gettext(
+        'vandelay.item.call_number.auto',
+        'Vandelay Generate Default Call Numbers',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'vandelay.item.call_number.auto',
+        'Auto-generate default item call numbers when no item call number is present',
+        'coust', 'label'),
+    'bool',
+    NULL
+), (
+    'vandelay',
+    'vandelay.item.call_number.prefix',
+    oils_i18n_gettext(
+        'vandelay.item.call_number.prefix',
+        'Vandelay Default Call Number Prefix',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'vandelay.item.call_number.prefix',
+        'Apply this prefix to any auto-generated item call numbers',
+        'coust', 'label'),
+    'string',
+    NULL
+), (
+    'vandelay',
+    'vandelay.item.copy_location.default',
+    oils_i18n_gettext(
+        'vandelay.item.copy_location.default',
+        'Vandelay Default Copy Location',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'vandelay.item.copy_location.default',
+        'Default copy location value for imported items',
+        'coust', 'label'),
+    'link',
+    'acpl'
+), (
+    'vandelay',
+    'vandelay.item.circ_modifier.default',
+    oils_i18n_gettext(
+        'vandelay.item.circ_modifier.default',
+        'Vandelay Default Circulation Modifier',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'vandelay.item.circ_modifier.default',
+        'Default circulation modifier value for imported items',
+        'coust', 'label'),
+    'link',
+    'ccm'
+);
+
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
+DECLARE
+
+    owning_lib      TEXT;
+    circ_lib        TEXT;
+    call_number     TEXT;
+    copy_number     TEXT;
+    status          TEXT;
+    location        TEXT;
+    circulate       TEXT;
+    deposit         TEXT;
+    deposit_amount  TEXT;
+    ref             TEXT;
+    holdable        TEXT;
+    price           TEXT;
+    barcode         TEXT;
+    circ_modifier   TEXT;
+    circ_as_type    TEXT;
+    alert_message   TEXT;
+    opac_visible    TEXT;
+    pub_note        TEXT;
+    priv_note       TEXT;
+    internal_id     TEXT;
+
+    attr_def        RECORD;
+    tmp_attr_set    RECORD;
+    attr_set        vandelay.import_item%ROWTYPE;
+
+    xpath           TEXT;
+    tmp_str         TEXT;
+
+BEGIN
+
+    SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
+
+    IF FOUND THEN
+
+        attr_set.definition := attr_def.id;
+
+        -- Build the combined XPath
+
+        owning_lib :=
+            CASE
+                WHEN attr_def.owning_lib IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
+            END;
+
+        circ_lib :=
+            CASE
+                WHEN attr_def.circ_lib IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
+            END;
+
+        call_number :=
+            CASE
+                WHEN attr_def.call_number IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
+            END;
+
+        copy_number :=
+            CASE
+                WHEN attr_def.copy_number IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
+            END;
+
+        status :=
+            CASE
+                WHEN attr_def.status IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
+            END;
+
+        location :=
+            CASE
+                WHEN attr_def.location IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
+            END;
+
+        circulate :=
+            CASE
+                WHEN attr_def.circulate IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
+            END;
+
+        deposit :=
+            CASE
+                WHEN attr_def.deposit IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
+            END;
+
+        deposit_amount :=
+            CASE
+                WHEN attr_def.deposit_amount IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
+            END;
+
+        ref :=
+            CASE
+                WHEN attr_def.ref IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
+            END;
+
+        holdable :=
+            CASE
+                WHEN attr_def.holdable IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
+            END;
+
+        price :=
+            CASE
+                WHEN attr_def.price IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
+            END;
+
+        barcode :=
+            CASE
+                WHEN attr_def.barcode IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
+            END;
+
+        circ_modifier :=
+            CASE
+                WHEN attr_def.circ_modifier IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
+            END;
+
+        circ_as_type :=
+            CASE
+                WHEN attr_def.circ_as_type IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
+            END;
+
+        alert_message :=
+            CASE
+                WHEN attr_def.alert_message IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
+            END;
+
+        opac_visible :=
+            CASE
+                WHEN attr_def.opac_visible IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
+            END;
+
+        pub_note :=
+            CASE
+                WHEN attr_def.pub_note IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
+            END;
+        priv_note :=
+            CASE
+                WHEN attr_def.priv_note IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
+            END;
+
+        internal_id :=
+            CASE
+                WHEN attr_def.internal_id IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.internal_id ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.internal_id || '"]'
+                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.internal_id
+            END;
+
+
+
+        xpath :=
+            owning_lib      || '|' ||
+            circ_lib        || '|' ||
+            call_number     || '|' ||
+            copy_number     || '|' ||
+            status          || '|' ||
+            location        || '|' ||
+            circulate       || '|' ||
+            deposit         || '|' ||
+            deposit_amount  || '|' ||
+            ref             || '|' ||
+            holdable        || '|' ||
+            price           || '|' ||
+            barcode         || '|' ||
+            circ_modifier   || '|' ||
+            circ_as_type    || '|' ||
+            alert_message   || '|' ||
+            pub_note        || '|' ||
+            priv_note       || '|' ||
+            internal_id     || '|' ||
+            opac_visible;
+
+        FOR tmp_attr_set IN
+                SELECT  *
+                  FROM  oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
+                            AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
+                                  dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
+                                  circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, internal_id TEXT, opac_vis TEXT )
+        LOOP
+
+            attr_set.import_error := NULL;
+            attr_set.error_detail := NULL;
+            attr_set.deposit_amount := NULL;
+            attr_set.copy_number := NULL;
+            attr_set.price := NULL;
+            attr_set.circ_modifier := NULL;
+            attr_set.location := NULL;
+            attr_set.barcode := NULL;
+            attr_set.call_number := NULL;
+
+            IF tmp_attr_set.pr != '' THEN
+                tmp_str = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
+                IF tmp_str = '' THEN 
+                    attr_set.import_error := 'import.item.invalid.price';
+                    attr_set.error_detail := tmp_attr_set.pr; -- original value
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+                attr_set.price := tmp_str::NUMERIC(8,2); 
+            END IF;
+
+            IF tmp_attr_set.dep_amount != '' THEN
+                tmp_str = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
+                IF tmp_str = '' THEN 
+                    attr_set.import_error := 'import.item.invalid.deposit_amount';
+                    attr_set.error_detail := tmp_attr_set.dep_amount; 
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+                attr_set.deposit_amount := tmp_str::NUMERIC(8,2); 
+            END IF;
+
+            IF tmp_attr_set.cnum != '' THEN
+                tmp_str = REGEXP_REPLACE(tmp_attr_set.cnum, E'[^0-9]', '', 'g');
+                IF tmp_str = '' THEN 
+                    attr_set.import_error := 'import.item.invalid.copy_number';
+                    attr_set.error_detail := tmp_attr_set.cnum; 
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+                attr_set.copy_number := tmp_str::INT; 
+            END IF;
+
+            IF tmp_attr_set.ol != '' THEN
+                SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.owning_lib';
+                    attr_set.error_detail := tmp_attr_set.ol;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+            END IF;
+
+            IF tmp_attr_set.clib != '' THEN
+                SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_lib';
+                    attr_set.error_detail := tmp_attr_set.clib;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+            END IF;
+
+            IF tmp_attr_set.cs != '' THEN
+                SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.status';
+                    attr_set.error_detail := tmp_attr_set.cs;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+            END IF;
+
+            IF COALESCE(tmp_attr_set.circ_mod, '') = '' THEN
+
+                -- no circ mod defined, see if we should apply a default
+                SELECT INTO attr_set.circ_modifier TRIM(BOTH '"' FROM value) 
+                    FROM actor.org_unit_ancestor_setting(
+                        'vandelay.item.circ_modifier.default', 
+                        attr_set.owning_lib
+                    );
+
+                -- make sure the value from the org setting is still valid
+                PERFORM 1 FROM config.circ_modifier WHERE code = attr_set.circ_modifier;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_modifier';
+                    attr_set.error_detail := tmp_attr_set.circ_mod;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+
+            ELSE 
+
+                SELECT code INTO attr_set.circ_modifier FROM config.circ_modifier WHERE code = tmp_attr_set.circ_mod;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_modifier';
+                    attr_set.error_detail := tmp_attr_set.circ_mod;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+            END IF;
+
+            IF tmp_attr_set.circ_as != '' THEN
+                SELECT code INTO attr_set.circ_as_type FROM config.coded_value_map WHERE ctype = 'item_type' AND code = tmp_attr_set.circ_as;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_as_type';
+                    attr_set.error_detail := tmp_attr_set.circ_as;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+            END IF;
+
+            IF COALESCE(tmp_attr_set.cl, '') = '' THEN
+                -- no location specified, see if we should apply a default
+
+                SELECT INTO attr_set.location TRIM(BOTH '"' FROM value) 
+                    FROM actor.org_unit_ancestor_setting(
+                        'vandelay.item.copy_location.default', 
+                        attr_set.owning_lib
+                    );
+
+                -- make sure the value from the org setting is still valid
+                PERFORM 1 FROM asset.copy_location WHERE id = attr_set.location;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.location';
+                    attr_set.error_detail := tmp_attr_set.cs;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+            ELSE
+
+                -- search up the org unit tree for a matching copy location
+                WITH RECURSIVE anscestor_depth AS (
+                    SELECT  ou.id,
+                        out.depth AS depth,
+                        ou.parent_ou
+                    FROM  actor.org_unit ou
+                        JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+                    WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
+                        UNION ALL
+                    SELECT  ou.id,
+                        out.depth,
+                        ou.parent_ou
+                    FROM  actor.org_unit ou
+                        JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+                        JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
+                ) SELECT  cpl.id INTO attr_set.location
+                    FROM  anscestor_depth a
+                        JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
+                    WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
+                    ORDER BY a.depth DESC
+                    LIMIT 1; 
+
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.location';
+                    attr_set.error_detail := tmp_attr_set.cs;
+                    RETURN NEXT attr_set; CONTINUE; 
+                END IF;
+            END IF;
+
+            attr_set.circulate      :=
+                LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
+
+            attr_set.deposit        :=
+                LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
+
+            attr_set.holdable       :=
+                LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
+
+            attr_set.opac_visible   :=
+                LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
+
+            attr_set.ref            :=
+                LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
+
+            attr_set.call_number    := tmp_attr_set.cn; -- TEXT
+            attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
+            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
+            attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
+            attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
+            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
+            attr_set.internal_id    := tmp_attr_set.internal_id::BIGINT;
+
+            RETURN NEXT attr_set;
+
+        END LOOP;
+
+    END IF;
+
+    RETURN;
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+-- Evergreen DB patch 0825.data.bre_format.sql
+--
+-- Fix some templates that loop over bibs to not have duplicated/run-on titles
+--
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0825', :eg_version);
+
+-- I think we shy away from modifying templates on existing systems, but this seems pretty safe...
+UPDATE
+    action_trigger.event_definition
+SET
+    template = replace(template,'[% FOR cbreb IN target %]','[% FOR cbreb IN target %][% title = '''' %]')
+WHERE
+    id IN (31,32);
+
+SELECT evergreen.upgrade_deps_block_check('0826', :eg_version);
+
+INSERT INTO permission.perm_list ( id, code, description ) VALUES (
+    551,
+    'ADMIN_SERVER_ADDON_FOR_WORKSTATION',
+    oils_i18n_gettext(
+        551,
+        'Allows a user to specify which Server Add-ons get invoked at the current workstation',
+        'ppl',
+        'description'
+    )
+);
+
+COMMIT;
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0827', :eg_version);
+
+ALTER TABLE action_trigger.event_definition ADD COLUMN repeat_delay INTERVAL;
+
+SELECT evergreen.upgrade_deps_block_check('0828', :eg_version);
+
+INSERT into config.org_unit_setting_type 
+    (name, grp, label, description, datatype)
+VALUES ( 
+    'opac.holds.org_unit_not_pickup_lib', 
+    'opac',
+    oils_i18n_gettext('opac.holds.org_unit_not_pickup_lib',
+        'OPAC: Org Unit is not a hold pickup library',
+        'coust', 'label'),
+    oils_i18n_gettext('opac.holds.org_unit_not_pickup_lib',
+        'If set, this org unit will not be offered to the patron as an '||
+        'option for a hold pickup location.  This setting has no affect '||
+        'on searching or hold targeting',
+        'coust', 'description'),
+    'bool'
+);
+
+-- Adds a setting for selecting the number of items per page of a my list.
+--
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0829', :eg_version);
+
+INSERT INTO config.usr_setting_type (name,opac_visible,label,description,datatype)
+    VALUES (
+        'opac.list_items_per_page',
+        TRUE,
+        oils_i18n_gettext(
+            'opac.list_items_per_page',
+            'List Items per Page',
+            'cust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'opac.list_items_per_page',
+            'A number designating the amount of list items displayed per page of a selected list.',
+            'cust',
+            'description'
+        ),
+        'string'
+    );
+
+--
+-- Adds a setting for selecting the number of lists per page for my list.
+--
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0830', :eg_version);
+
+INSERT INTO config.usr_setting_type (name,opac_visible,label,description,datatype)
+    VALUES (
+        'opac.lists_per_page',
+        TRUE,
+        oils_i18n_gettext(
+            'opac.lists_per_page',
+            'Lists per Page',
+            'cust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'opac.lists_per_page',
+            'A number designating the amount of lists displayed per page.',
+            'cust',
+            'description'
+        ),
+        'string'
+    );
+    
+SELECT evergreen.upgrade_deps_block_check('0831', :eg_version);
+
+-- TODO: check for penalty ID collision before master merge; affects 
+-- config.standing_penalty and actor.calculate_system_penalties
+
+INSERT INTO config.standing_penalty
+    (id, name, label, block_list, staff_alert)
+VALUES (
+    35,
+    'PATRON_EXCEEDS_LONGOVERDUE_COUNT',
+    oils_i18n_gettext(
+        35,
+        'Patron Exceeds Max Long-Overdue Threshold',
+        'csp',
+        'label'
+    ),
+    'CIRC|FULFILL|HOLD|CAPTURE|RENEW',
+    TRUE
+);
+
+
+CREATE OR REPLACE FUNCTION actor.calculate_system_penalties( match_user INT, context_org INT ) RETURNS SETOF actor.usr_standing_penalty AS $func$
+DECLARE
+    user_object         actor.usr%ROWTYPE;
+    new_sp_row          actor.usr_standing_penalty%ROWTYPE;
+    existing_sp_row     actor.usr_standing_penalty%ROWTYPE;
+    collections_fines   permission.grp_penalty_threshold%ROWTYPE;
+    max_fines           permission.grp_penalty_threshold%ROWTYPE;
+    max_overdue         permission.grp_penalty_threshold%ROWTYPE;
+    max_items_out       permission.grp_penalty_threshold%ROWTYPE;
+    max_lost            permission.grp_penalty_threshold%ROWTYPE;
+    max_longoverdue     permission.grp_penalty_threshold%ROWTYPE;
+    tmp_grp             INT;
+    items_overdue       INT;
+    items_out           INT;
+    items_lost          INT;
+    items_longoverdue   INT;
+    context_org_list    INT[];
+    current_fines        NUMERIC(8,2) := 0.0;
+    tmp_fines            NUMERIC(8,2);
+    tmp_groc            RECORD;
+    tmp_circ            RECORD;
+    tmp_org             actor.org_unit%ROWTYPE;
+    tmp_penalty         config.standing_penalty%ROWTYPE;
+    tmp_depth           INTEGER;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+
+    -- Max fines
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has a high fine balance
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 1 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_fines.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 1;
+
+        SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                      FROM  booking.reservation r
+                      WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                      FROM  money.grocery g
+                      WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                      FROM  action.circulation circ
+                      WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL ) l USING (id);
+
+        IF current_fines >= max_fines.threshold THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_fines.org_unit;
+            new_sp_row.standing_penalty := 1;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max overdue
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many overdue items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+
+            SELECT * INTO max_overdue FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 2 AND org_unit = tmp_org.id;
+
+            IF max_overdue.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_overdue.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_overdue.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_overdue.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 2;
+
+        SELECT  INTO items_overdue COUNT(*)
+          FROM  action.circulation circ
+                JOIN  actor.org_unit_full_path( max_overdue.org_unit ) fp ON (circ.circ_lib = fp.id)
+          WHERE circ.usr = match_user
+            AND circ.checkin_time IS NULL
+            AND circ.due_date < NOW()
+            AND (circ.stop_fines = 'MAXFINES' OR circ.stop_fines IS NULL);
+
+        IF items_overdue >= max_overdue.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_overdue.org_unit;
+            new_sp_row.standing_penalty := 2;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max out
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many checked out items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_items_out FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 3 AND org_unit = tmp_org.id;
+
+            IF max_items_out.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_items_out.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+
+    -- Fail if the user has too many items checked out
+    IF max_items_out.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_items_out.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 3;
+
+        SELECT  INTO items_out COUNT(*)
+          FROM  action.circulation circ
+                JOIN  actor.org_unit_full_path( max_items_out.org_unit ) fp ON (circ.circ_lib = fp.id)
+          WHERE circ.usr = match_user
+                AND circ.checkin_time IS NULL
+                AND (circ.stop_fines IN (
+                    SELECT 'MAXFINES'::TEXT
+                    UNION ALL
+                    SELECT 'LONGOVERDUE'::TEXT
+                    UNION ALL
+                    SELECT 'LOST'::TEXT
+                    WHERE 'true' ILIKE
+                    (
+                        SELECT CASE
+                            WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.tally_lost', circ.circ_lib)) ILIKE 'true' THEN 'true'
+                            ELSE 'false'
+                        END
+                    )
+                    UNION ALL
+                    SELECT 'CLAIMSRETURNED'::TEXT
+                    WHERE 'false' ILIKE
+                    (
+                        SELECT CASE
+                            WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.do_not_tally_claims_returned', circ.circ_lib)) ILIKE 'true' THEN 'true'
+                            ELSE 'false'
+                        END
+                    )
+                    ) OR circ.stop_fines IS NULL)
+                AND xact_finish IS NULL;
+
+           IF items_out >= max_items_out.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_items_out.org_unit;
+            new_sp_row.standing_penalty := 3;
+            RETURN NEXT new_sp_row;
+           END IF;
+    END IF;
+
+    -- Start over for max lost
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many lost items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+
+            SELECT * INTO max_lost FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 5 AND org_unit = tmp_org.id;
+
+            IF max_lost.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_lost.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_lost.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+            FROM  actor.usr_standing_penalty
+            WHERE usr = match_user
+                AND org_unit = max_lost.org_unit
+                AND (stop_date IS NULL or stop_date > NOW())
+                AND standing_penalty = 5;
+
+        SELECT  INTO items_lost COUNT(*)
+        FROM  action.circulation circ
+            JOIN  actor.org_unit_full_path( max_lost.org_unit ) fp ON (circ.circ_lib = fp.id)
+        WHERE circ.usr = match_user
+            AND circ.checkin_time IS NULL
+            AND (circ.stop_fines = 'LOST')
+            AND xact_finish IS NULL;
+
+        IF items_lost >= max_lost.threshold::INT AND 0 < max_lost.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_lost.org_unit;
+            new_sp_row.standing_penalty := 5;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max longoverdue
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many longoverdue items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+
+            SELECT * INTO max_longoverdue 
+                FROM permission.grp_penalty_threshold 
+                WHERE grp = tmp_grp AND 
+                    penalty = 35 AND 
+                    org_unit = tmp_org.id;
+
+            IF max_longoverdue.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp 
+                    FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_longoverdue.threshold IS NOT NULL 
+                OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_longoverdue.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+            FROM  actor.usr_standing_penalty
+            WHERE usr = match_user
+                AND org_unit = max_longoverdue.org_unit
+                AND (stop_date IS NULL or stop_date > NOW())
+                AND standing_penalty = 35;
+
+        SELECT INTO items_longoverdue COUNT(*)
+        FROM action.circulation circ
+            JOIN actor.org_unit_full_path( max_longoverdue.org_unit ) fp 
+                ON (circ.circ_lib = fp.id)
+        WHERE circ.usr = match_user
+            AND circ.checkin_time IS NULL
+            AND (circ.stop_fines = 'LONGOVERDUE')
+            AND xact_finish IS NULL;
+
+        IF items_longoverdue >= max_longoverdue.threshold::INT 
+                AND 0 < max_longoverdue.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_longoverdue.org_unit;
+            new_sp_row.standing_penalty := 35;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+
+    -- Start over for collections warning
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has a collections-level fine balance
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 4 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_fines.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 4;
+
+        SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                      FROM  booking.reservation r
+                      WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND r.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                      FROM  money.grocery g
+                      WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND g.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                      FROM  action.circulation circ
+                      WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND circ.xact_finish IS NULL ) l USING (id);
+
+        IF current_fines >= max_fines.threshold THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_fines.org_unit;
+            new_sp_row.standing_penalty := 4;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for in collections
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Remove the in-collections penalty if the user has paid down enough
+    -- This penalty is different, because this code is not responsible for creating 
+    -- new in-collections penalties, only for removing them
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 30 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        -- first, see if the user had paid down to the threshold
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                      FROM  booking.reservation r
+                      WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND r.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                      FROM  money.grocery g
+                      WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND g.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                      FROM  action.circulation circ
+                      WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND circ.xact_finish IS NULL ) l USING (id);
+
+        IF current_fines IS NULL OR current_fines <= max_fines.threshold THEN
+            -- patron has paid down enough
+
+            SELECT INTO tmp_penalty * FROM config.standing_penalty WHERE id = 30;
+
+            IF tmp_penalty.org_depth IS NOT NULL THEN
+
+                -- since this code is not responsible for applying the penalty, it can't 
+                -- guarantee the current context org will match the org at which the penalty 
+                --- was applied.  search up the org tree until we hit the configured penalty depth
+                SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+                SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+
+                WHILE tmp_depth >= tmp_penalty.org_depth LOOP
+
+                    RETURN QUERY
+                        SELECT  *
+                          FROM  actor.usr_standing_penalty
+                          WHERE usr = match_user
+                                AND org_unit = tmp_org.id
+                                AND (stop_date IS NULL or stop_date > NOW())
+                                AND standing_penalty = 30;
+
+                    IF tmp_org.parent_ou IS NULL THEN
+                        EXIT;
+                    END IF;
+
+                    SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+                    SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+                END LOOP;
+
+            ELSE
+
+                -- no penalty depth is defined, look for exact matches
+
+                RETURN QUERY
+                    SELECT  *
+                      FROM  actor.usr_standing_penalty
+                      WHERE usr = match_user
+                            AND org_unit = max_fines.org_unit
+                            AND (stop_date IS NULL or stop_date > NOW())
+                            AND standing_penalty = 30;
+            END IF;
+    
+        END IF;
+
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE plpgsql;
+
+SELECT evergreen.upgrade_deps_block_check('0832', :eg_version);
+
+ALTER TABLE serial.subscription_note
+       ADD COLUMN alert BOOL NOT NULL DEFAULT FALSE;
+
+ALTER TABLE serial.distribution_note
+       ADD COLUMN alert BOOL NOT NULL DEFAULT FALSE;
+
+ALTER TABLE serial.item_note
+       ADD COLUMN alert BOOL NOT NULL DEFAULT FALSE;
+
+SELECT evergreen.upgrade_deps_block_check('0833', :eg_version);
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, datatype, label, description)
+VALUES (
+    'opac.self_register.timeout',
+    'opac',
+    'integer',
+    oils_i18n_gettext(
+        'opac.self_register.timeout',
+        'Patron Self-Reg. Display Timeout',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'opac.self_register.timeout',
+        'Number of seconds to wait before reloading the patron self-'||
+        'registration interface to clear sensitive data',
+        'coust',
+        'description'
+    )
+);
+
+SELECT evergreen.upgrade_deps_block_check('0834', :eg_version);
+
+INSERT INTO config.org_unit_setting_type 
+    (name, grp, datatype, label, description)
+VALUES (
+    'ui.circ.items_out.longoverdue', 'gui', 'integer',
+    oils_i18n_gettext(
+        'ui.circ.items_out.longoverdue',
+        'Items Out Long-Overdue display setting',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ui.circ.items_out.longoverdue',
+'Value is a numeric code, describing which list the circulation '||
+'should appear while checked out and whether the circulation should '||
+'continue to appear in the bottom list, when checked in with '||
+'oustanding fines.  '||
+'1 = top list, bottom list.  2 = bottom list, bottom list.  ' ||
+'5 = top list, do not display.  6 = bottom list, do not display.',
+        'coust',
+        'description'
+    )
+), (
+    'ui.circ.items_out.lost', 'gui', 'integer',
+    oils_i18n_gettext(
+        'ui.circ.items_out.lost',
+        'Items Out Lost display setting',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ui.circ.items_out.lost',
+'Value is a numeric code, describing which list the circulation '||
+'should appear while checked out and whether the circulation should '||
+'continue to appear in the bottom list, when checked in with '||
+'oustanding fines.  '||
+'1 = top list, bottom list.  2 = bottom list, bottom list.  ' ||
+'5 = top list, do not display.  6 = bottom list, do not display.',
+        'coust',
+        'description'
+    )
+), (
+    'ui.circ.items_out.claimsreturned', 'gui', 'integer',
+    oils_i18n_gettext(
+        'ui.circ.items_out.claimsreturned',
+        'Items Out Claims Returned display setting',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ui.circ.items_out.claimsreturned',
+'Value is a numeric code, describing which list the circulation '||
+'should appear while checked out and whether the circulation should '||
+'continue to appear in the bottom list, when checked in with '||
+'oustanding fines.  '||
+'1 = top list, bottom list.  2 = bottom list, bottom list.  ' ||
+'5 = top list, do not display.  6 = bottom list, do not display.',
+        'coust',
+        'description'
+    )
+);
+
+SELECT evergreen.upgrade_deps_block_check('0835', :eg_version);
+
+INSERT INTO config.org_unit_setting_type 
+    (grp, name, datatype, label, description) 
+VALUES (
+    'finance',
+    'circ.disable_patron_credit',
+    'bool',
+    oils_i18n_gettext(
+        'circ.disable_patron_credit',
+        'Disable Patron Credit',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'circ.disable_patron_credit',
+        'Do not allow patrons to accrue credit or pay fines/fees with accrued credit',
+        'coust',
+        'description'
+    )
+);
+
+SELECT evergreen.upgrade_deps_block_check('0836', :eg_version);
+
+CREATE TABLE config.floating_group (
+    id      SERIAL PRIMARY KEY, 
+    name    TEXT UNIQUE NOT NULL,
+    manual  BOOL NOT NULL DEFAULT FALSE
+    );
+
+CREATE TABLE config.floating_group_member (
+    id              SERIAL PRIMARY KEY,
+    floating_group  INT NOT NULL REFERENCES config.floating_group (id),
+    org_unit        INT NOT NULL REFERENCES actor.org_unit (id),
+    stop_depth      INT NOT NULL DEFAULT 0,
+    max_depth       INT,
+    exclude         BOOL NOT NULL DEFAULT FALSE
+    );
+
+CREATE OR REPLACE FUNCTION evergreen.can_float( copy_floating_group integer, from_ou integer, to_ou integer ) RETURNS BOOL AS $f$
+DECLARE
+    float_member config.floating_group_member%ROWTYPE;
+    shared_ou_depth INT;
+    to_ou_depth INT;
+BEGIN
+    -- Grab the shared OU depth. If this is less than the stop depth later we ignore the entry.
+    SELECT INTO shared_ou_depth max(depth) FROM actor.org_unit_common_ancestors( from_ou, to_ou ) aou JOIN actor.org_unit_type aout ON aou.ou_type = aout.id;
+    -- Grab the to ou depth. If this is greater than max depth we ignore the entry.
+    SELECT INTO to_ou_depth depth FROM actor.org_unit aou JOIN actor.org_unit_type aout ON aou.ou_type = aout.id WHERE aou.id = to_ou;
+    -- Grab float members that apply. We don't care what we get beyond wanting excluded ones first.
+    SELECT INTO float_member *
+        FROM
+            config.floating_group_member cfgm
+            JOIN actor.org_unit aou ON cfgm.org_unit = aou.id
+            JOIN actor.org_unit_type aout ON aou.ou_type = aout.id
+        WHERE
+            cfgm.floating_group = copy_floating_group
+            AND to_ou IN (SELECT id FROM actor.org_unit_descendants(aou.id))
+            AND cfgm.stop_depth <= shared_ou_depth
+            AND (cfgm.max_depth IS NULL OR to_ou_depth <= max_depth)
+        ORDER BY
+            exclude DESC;
+    -- If we found something then we want to return the opposite of the exclude flag
+    IF FOUND THEN
+        RETURN NOT float_member.exclude;
+    END IF;
+    -- Otherwise no floating.
+    RETURN false;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+\qecho **** UPDATING asset.copy and auditor.asset_copy_history could take some time
+
+INSERT INTO config.floating_group(name) VALUES ('Everywhere');
+INSERT INTO config.floating_group_member(floating_group, org_unit) VALUES (1, 1);
+
+-- We need to drop these before we can update asset.copy
+DROP VIEW auditor.asset_copy_lifecycle;
+DROP VIEW auditor.serial_unit_lifecycle;
+
+-- Update the appropriate auditor tables
+ALTER TABLE auditor.asset_copy_history
+    ALTER COLUMN floating DROP DEFAULT,
+    ALTER COLUMN floating DROP NOT NULL,
+    ALTER COLUMN floating TYPE int USING CASE WHEN floating THEN 1 ELSE NULL END;
+ALTER TABLE auditor.serial_unit_history
+    ALTER COLUMN floating DROP DEFAULT,
+    ALTER COLUMN floating DROP NOT NULL,
+    ALTER COLUMN floating TYPE int USING CASE WHEN floating THEN 1 ELSE NULL END;
+
+-- Update asset.copy itself (does not appear to trigger update triggers!)
+ALTER TABLE asset.copy
+    ALTER COLUMN floating DROP DEFAULT,
+    ALTER COLUMN floating DROP NOT NULL,
+    ALTER COLUMN floating TYPE int USING CASE WHEN floating THEN 1 ELSE NULL END;
+
+ALTER TABLE asset.copy ADD CONSTRAINT asset_copy_floating_fkey FOREIGN KEY (floating) REFERENCES config.floating_group (id) DEFERRABLE INITIALLY DEFERRED;
+
+-- Update asset.copy_template too
+ALTER TABLE asset.copy_template
+    ALTER COLUMN floating TYPE int USING CASE WHEN floating THEN 1 ELSE NULL END;
+ALTER TABLE asset.copy_template ADD CONSTRAINT asset_copy_template_floating_fkey FOREIGN KEY (floating) REFERENCES config.floating_group (id) DEFERRABLE INITIALLY DEFERRED;
+
+INSERT INTO permission.perm_list( code, description) VALUES
+('ADMIN_FLOAT_GROUPS', 'Allows administration of floating groups');
+
+-- And lets just update all auditors to re-create those lifecycle views
+SELECT auditor.update_auditors();
+
+\qecho **** FINISHED UPDATING asset.copy and auditor.asset_copy_history
+
+-- Evergreen DB patch 0837.schema.browse-auth-linking.plus-joiner.sql
+--
+-- In this upgrade script we complete inter-subfield joiner support, so that
+-- subject components can be separated by " -- ", for instance.  That's the
+-- easy part.
+--
+-- We also add the ability to browse by in-use authority main entries and find
+-- bibs that use unauthorized versions of the authority's value, by string matching.
+--
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0837', :eg_version);
+
+ALTER TABLE config.metabib_field ADD COLUMN joiner TEXT;
+UPDATE config.metabib_field SET joiner = ' -- ' WHERE field_class = 'subject' AND name NOT IN ('name', 'complete');
+
+-- To avoid problems with altering a table column after doing an
+-- update.
+ALTER TABLE authority.control_set_authority_field DISABLE TRIGGER ALL;
+
+ALTER TABLE authority.control_set_authority_field ADD COLUMN joiner TEXT;
+UPDATE authority.control_set_authority_field SET joiner = ' -- ' WHERE tag LIKE ANY (ARRAY['_4_','_5_','_8_']);
+
+ALTER TABLE authority.control_set_authority_field ENABLE TRIGGER ALL;
+
+-- Seed data will be generated from class <-> axis mapping
+CREATE TABLE authority.control_set_bib_field_metabib_field_map (
+    id              SERIAL  PRIMARY KEY,
+    bib_field       INT     NOT NULL REFERENCES authority.control_set_bib_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    metabib_field   INT     NOT NULL REFERENCES config.metabib_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    CONSTRAINT a_bf_mf_map_once UNIQUE (bib_field, metabib_field)
+);
+
+CREATE VIEW authority.control_set_auth_field_metabib_field_map_main AS
+    SELECT  DISTINCT b.authority_field, m.metabib_field
+      FROM  authority.control_set_bib_field_metabib_field_map m JOIN authority.control_set_bib_field b ON (b.id = m.bib_field);
+COMMENT ON VIEW authority.control_set_auth_field_metabib_field_map_main IS $$metabib fields for main entry auth fields$$;
+
+CREATE VIEW authority.control_set_auth_field_metabib_field_map_refs_only AS
+    SELECT  DISTINCT a.id AS authority_field, m.metabib_field
+      FROM  authority.control_set_authority_field a
+            JOIN authority.control_set_authority_field ame ON (a.main_entry = ame.id)
+            JOIN authority.control_set_bib_field b ON (b.authority_field = ame.id)
+            JOIN authority.control_set_bib_field_metabib_field_map mf ON (mf.bib_field = b.id)
+            JOIN authority.control_set_auth_field_metabib_field_map_main m ON (ame.id = m.authority_field);
+COMMENT ON VIEW authority.control_set_auth_field_metabib_field_map_refs_only IS $$metabib fields for NON-main entry auth fields$$;
+
+CREATE VIEW authority.control_set_auth_field_metabib_field_map_refs AS
+    SELECT * FROM authority.control_set_auth_field_metabib_field_map_main
+        UNION
+    SELECT * FROM authority.control_set_auth_field_metabib_field_map_refs_only;
+COMMENT ON VIEW authority.control_set_auth_field_metabib_field_map_refs IS $$metabib fields for all auth fields$$;
+
+
+-- blind refs only is probably what we want for lookup in bib/auth browse
+CREATE VIEW authority.control_set_auth_field_metabib_field_map_blind_refs_only AS
+    SELECT  r.*
+      FROM  authority.control_set_auth_field_metabib_field_map_refs_only r
+            JOIN authority.control_set_authority_field a ON (r.authority_field = a.id)
+      WHERE linking_subfield IS NULL;
+COMMENT ON VIEW authority.control_set_auth_field_metabib_field_map_blind_refs_only IS $$metabib fields for NON-main entry auth fields that can't be linked to other records$$; -- '
+
+CREATE VIEW authority.control_set_auth_field_metabib_field_map_blind_refs AS
+    SELECT  r.*
+      FROM  authority.control_set_auth_field_metabib_field_map_refs r
+            JOIN authority.control_set_authority_field a ON (r.authority_field = a.id)
+      WHERE linking_subfield IS NULL;
+COMMENT ON VIEW authority.control_set_auth_field_metabib_field_map_blind_refs IS $$metabib fields for all auth fields that can't be linked to other records$$; -- '
+
+CREATE VIEW authority.control_set_auth_field_metabib_field_map_blind_main AS
+    SELECT  r.*
+      FROM  authority.control_set_auth_field_metabib_field_map_main r
+            JOIN authority.control_set_authority_field a ON (r.authority_field = a.id)
+      WHERE linking_subfield IS NULL;
+COMMENT ON VIEW authority.control_set_auth_field_metabib_field_map_blind_main IS $$metabib fields for main entry auth fields that can't be linked to other records$$; -- '
+
+CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
+DECLARE
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    sf_node         TEXT;
+    tag_node        TEXT;
+    thes_code       TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    tmp_text        TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT;
+BEGIN
+    SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    IF cset IS NULL THEN
+        SELECT  control_set INTO cset
+          FROM  authority.control_set_authority_field
+          WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
+          LIMIT 1;
+    END IF;
+
+    thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
+    IF thes_code IS NULL THEN
+        thes_code := '|';
+    ELSIF thes_code = 'z' THEN
+        thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
+    END IF;
+
+    heading_text := '';
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+        first_sf := TRUE;
+
+        FOR tag_node IN SELECT unnest(oils_xpath('//*[@tag="'||tag_used||'"]',marcxml)) LOOP
+            FOR sf_node IN SELECT unnest(oils_xpath('./*[contains("'||acsaf.sf_list||'",@code)]',tag_node)) LOOP
+
+                tmp_text := oils_xpath_string('.', sf_node);
+                sf := oils_xpath_string('./@code', sf_node);
+
+                IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
+
+                    tmp_text := SUBSTRING(
+                        tmp_text FROM
+                        COALESCE(
+                            NULLIF(
+                                REGEXP_REPLACE(
+                                    oils_xpath_string('./@ind'||nfi_used, tag_node),
+                                    $$\D+$$,
+                                    '',
+                                    'g'
+                                ),
+                                ''
+                            )::INT,
+                            0
+                        ) + 1
+                    );
+
+                END IF;
+
+                first_sf := FALSE;
+
+                IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
+                    heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
+                END IF;
+            END LOOP;
+
+            EXIT WHEN heading_text <> '';
+        END LOOP;
+
+        EXIT WHEN heading_text <> '';
+    END LOOP;
+
+    IF heading_text <> '' THEN
+        IF no_thesaurus IS TRUE THEN
+            heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
+        ELSE
+            heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
+        END IF;
+    ELSE
+        heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
+    END IF;
+
+    RETURN heading_text;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
+DECLARE
+    res             authority.simple_heading%ROWTYPE;
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    joiner_text    TEXT;
+    sort_text       TEXT;
+    tmp_text        TEXT;
+    tmp_xml         TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT;
+BEGIN
+
+    SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    IF cset IS NULL THEN
+        SELECT  control_set INTO cset
+          FROM  authority.control_set_authority_field
+          WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
+          LIMIT 1;
+    END IF;
+
+    res.record := auth_id;
+
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
+
+        res.atag := acsaf.id;
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+        joiner_text := COALESCE(acsaf.joiner, ' ');
+
+        FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
+
+            heading_text := COALESCE(
+                oils_xpath_string('./*[contains("'||acsaf.sf_list||'",@code)]', tmp_xml::TEXT, joiner_text),
+                ''
+            );
+
+            IF nfi_used IS NOT NULL THEN
+
+                sort_text := SUBSTRING(
+                    heading_text FROM
+                    COALESCE(
+                        NULLIF(
+                            REGEXP_REPLACE(
+                                oils_xpath_string('./@ind'||nfi_used, tmp_xml::TEXT),
+                                $$\D+$$,
+                                '',
+                                'g'
+                            ),
+                            ''
+                        )::INT,
+                        0
+                    ) + 1
+                );
+
+            ELSE
+                sort_text := heading_text;
+            END IF;
+
+            IF heading_text IS NOT NULL AND heading_text <> '' THEN
+                res.value := heading_text;
+                res.sort_value := public.naco_normalize(sort_text);
+                res.index_vector = to_tsvector('keyword'::regconfig, res.sort_value);
+                RETURN NEXT res;
+            END IF;
+
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+CREATE TABLE metabib.browse_entry_simple_heading_map (
+    id BIGSERIAL PRIMARY KEY,
+    entry BIGINT REFERENCES metabib.browse_entry (id),
+    simple_heading BIGINT REFERENCES authority.simple_heading (id) ON DELETE CASCADE
+);
+CREATE INDEX browse_entry_sh_map_entry_idx ON metabib.browse_entry_simple_heading_map (entry);
+CREATE INDEX browse_entry_sh_map_sh_idx ON metabib.browse_entry_simple_heading_map (simple_heading);
+
+CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
+DECLARE
+    bib     biblio.record_entry%ROWTYPE;
+    idx     config.metabib_field%ROWTYPE;
+    xfrm        config.xml_transform%ROWTYPE;
+    prev_xfrm   TEXT;
+    transformed_xml TEXT;
+    xml_node    TEXT;
+    xml_node_list   TEXT[];
+    facet_text  TEXT;
+    browse_text TEXT;
+    sort_value  TEXT;
+    raw_text    TEXT;
+    curr_text   TEXT;
+    joiner      TEXT := default_joiner; -- XXX will index defs supply a joiner?
+    authority_text TEXT;
+    authority_link BIGINT;
+    output_row  metabib.field_entry_template%ROWTYPE;
+BEGIN
+
+    -- Start out with no field-use bools set
+    output_row.browse_field = FALSE;
+    output_row.facet_field = FALSE;
+    output_row.search_field = FALSE;
+
+    -- Get the record
+    SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
+
+    -- Loop over the indexing entries
+    FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
+
+        joiner := COALESCE(idx.joiner, default_joiner);
+
+        SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
+
+        -- See if we can skip the XSLT ... it's expensive
+        IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+            -- Can't skip the transform
+            IF xfrm.xslt <> '---' THEN
+                transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
+            ELSE
+                transformed_xml := bib.marc;
+            END IF;
+
+            prev_xfrm := xfrm.name;
+        END IF;
+
+        xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+
+        raw_text := NULL;
+        FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
+            CONTINUE WHEN xml_node !~ E'^\\s*<';
+
+            -- XXX much of this should be moved into oils_xpath_string...
+            curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
+                oils_xpath( '//text()',
+                    REGEXP_REPLACE(
+                        REGEXP_REPLACE( -- This escapes all &s not followed by "amp;".  Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
+                            REGEXP_REPLACE( -- This escapes embeded <s
+                                xml_node,
+                                $re$(>[^<]+)(<)([^>]+<)$re$,
+                                E'\\1&lt;\\3',
+                                'g'
+                            ),
+                            '&(?!amp;)',
+                            '&amp;',
+                            'g'
+                        ),
+                        E'\\s+',
+                        ' ',
+                        'g'
+                    )
+                ), ' '), ''),
+                joiner
+            );
+
+            CONTINUE WHEN curr_text IS NULL OR curr_text = '';
+
+            IF raw_text IS NOT NULL THEN
+                raw_text := raw_text || joiner;
+            END IF;
+
+            raw_text := COALESCE(raw_text,'') || curr_text;
+
+            -- autosuggest/metabib.browse_entry
+            IF idx.browse_field THEN
+
+                IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
+                    browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    browse_text := curr_text;
+                END IF;
+
+                IF idx.browse_sort_xpath IS NOT NULL AND
+                    idx.browse_sort_xpath <> '' THEN
+
+                    sort_value := oils_xpath_string(
+                        idx.browse_sort_xpath, xml_node, joiner,
+                        ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
+                    );
+                ELSE
+                    sort_value := browse_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
+                output_row.sort_value :=
+                    public.naco_normalize(sort_value);
+
+                output_row.authority := NULL;
+
+                IF idx.authority_xpath IS NOT NULL AND idx.authority_xpath <> '' THEN
+                    authority_text := oils_xpath_string(
+                        idx.authority_xpath, xml_node, joiner,
+                        ARRAY[
+                            ARRAY[xfrm.prefix, xfrm.namespace_uri],
+                            ARRAY['xlink','http://www.w3.org/1999/xlink']
+                        ]
+                    );
+
+                    IF authority_text ~ '^\d+$' THEN
+                        authority_link := authority_text::BIGINT;
+                        PERFORM * FROM authority.record_entry WHERE id = authority_link;
+                        IF FOUND THEN
+                            output_row.authority := authority_link;
+                        END IF;
+                    END IF;
+
+                END IF;
+
+                output_row.browse_field = TRUE;
+                RETURN NEXT output_row;
+                output_row.browse_field = FALSE;
+                output_row.sort_value := NULL;
+            END IF;
+
+            -- insert raw node text for faceting
+            IF idx.facet_field THEN
+
+                IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
+                    facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    facet_text := curr_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = -1 * idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
+
+                output_row.facet_field = TRUE;
+                RETURN NEXT output_row;
+                output_row.facet_field = FALSE;
+            END IF;
+
+        END LOOP;
+
+        CONTINUE WHEN raw_text IS NULL OR raw_text = '';
+
+        -- insert combined node text for searching
+        IF idx.search_field THEN
+            output_row.field_class = idx.field_class;
+            output_row.field = idx.id;
+            output_row.source = rid;
+            output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
+
+            output_row.search_field = TRUE;
+            RETURN NEXT output_row;
+            output_row.search_field = FALSE;
+        END IF;
+
+    END LOOP;
+
+END;
+
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE
+    FUNCTION metabib.autosuggest_prepare_tsquery(orig TEXT) RETURNS TEXT[] AS
+$$
+DECLARE
+    orig_ended_in_space     BOOLEAN;
+    result                  RECORD;
+    plain                   TEXT;
+    normalized              TEXT;
+BEGIN
+    orig_ended_in_space := orig ~ E'\\s$';
+
+    orig := ARRAY_TO_STRING(
+        evergreen.regexp_split_to_array(orig, E'\\W+'), ' '
+    );
+
+    normalized := public.naco_normalize(orig); -- also trim()s
+    plain := trim(orig);
+
+    IF NOT orig_ended_in_space THEN
+        plain := plain || ':*';
+        normalized := normalized || ':*';
+    END IF;
+
+    plain := ARRAY_TO_STRING(
+        evergreen.regexp_split_to_array(plain, E'\\s+'), ' & '
+    );
+    normalized := ARRAY_TO_STRING(
+        evergreen.regexp_split_to_array(normalized, E'\\s+'), ' & '
+    );
+
+    RETURN ARRAY[normalized, plain];
+END;
+$$ LANGUAGE PLPGSQL;
+
+ALTER TYPE metabib.flat_browse_entry_appearance ADD ATTRIBUTE sees TEXT;
+ALTER TYPE metabib.flat_browse_entry_appearance ADD ATTRIBUTE asources INT;
+ALTER TYPE metabib.flat_browse_entry_appearance ADD ATTRIBUTE aaccurate TEXT;
+
+CREATE OR REPLACE FUNCTION metabib.browse_bib_pivot(
+    INT[],
+    TEXT
+) RETURNS BIGINT AS $p$
+    SELECT  mbe.id
+      FROM  metabib.browse_entry mbe
+            JOIN metabib.browse_entry_def_map mbedm ON (
+                mbedm.entry = mbe.id
+                AND mbedm.def = ANY($1)
+            )
+      WHERE mbe.sort_value >= public.naco_normalize($2)
+      ORDER BY mbe.sort_value, mbe.value LIMIT 1;
+$p$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION metabib.browse_authority_pivot(
+    INT[],
+    TEXT
+) RETURNS BIGINT AS $p$
+    SELECT  mbe.id
+      FROM  metabib.browse_entry mbe
+            JOIN metabib.browse_entry_simple_heading_map mbeshm ON ( mbeshm.entry = mbe.id )
+            JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
+            JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
+                ash.atag = map.authority_field
+                AND map.metabib_field = ANY($1)
+            )
+      WHERE mbe.sort_value >= public.naco_normalize($2)
+      ORDER BY mbe.sort_value, mbe.value LIMIT 1;
+$p$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION metabib.browse_authority_refs_pivot(
+    INT[],
+    TEXT
+) RETURNS BIGINT AS $p$
+    SELECT  mbe.id
+      FROM  metabib.browse_entry mbe
+            JOIN metabib.browse_entry_simple_heading_map mbeshm ON ( mbeshm.entry = mbe.id )
+            JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
+            JOIN authority.control_set_auth_field_metabib_field_map_refs_only map ON (
+                ash.atag = map.authority_field
+                AND map.metabib_field = ANY($1)
+            )
+      WHERE mbe.sort_value >= public.naco_normalize($2)
+      ORDER BY mbe.sort_value, mbe.value LIMIT 1;
+$p$ LANGUAGE SQL;
+
+-- The drop is necessary because the language change from PLPGSQL to SQL
+-- carries with it name changes to the parameters
+DROP FUNCTION metabib.browse_pivot(INT[], TEXT);
+CREATE FUNCTION metabib.browse_pivot(
+    INT[],
+    TEXT
+) RETURNS BIGINT AS $p$
+    SELECT  id FROM metabib.browse_entry
+      WHERE id IN (
+                metabib.browse_bib_pivot($1, $2),
+                metabib.browse_authority_refs_pivot($1,$2) -- only look in 4xx, 5xx, 7xx of authority
+            )
+      ORDER BY sort_value, value LIMIT 1;
+$p$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION metabib.staged_browse(
+    query                   TEXT,
+    fields                  INT[],
+    context_org             INT,
+    context_locations       INT[],
+    staff                   BOOL,
+    browse_superpage_size   INT,
+    count_up_from_zero      BOOL,   -- if false, count down from -1
+    result_limit            INT,
+    next_pivot_pos          INT
+) RETURNS SETOF metabib.flat_browse_entry_appearance AS $p$
+DECLARE
+    curs                    REFCURSOR;
+    rec                     RECORD;
+    qpfts_query             TEXT;
+    aqpfts_query            TEXT;
+    afields                 INT[];
+    bfields                 INT[];
+    result_row              metabib.flat_browse_entry_appearance%ROWTYPE;
+    results_skipped         INT := 0;
+    row_counter             INT := 0;
+    row_number              INT;
+    slice_start             INT;
+    slice_end               INT;
+    full_end                INT;
+    all_records             BIGINT[];
+    all_brecords             BIGINT[];
+    all_arecords            BIGINT[];
+    superpage_of_records    BIGINT[];
+    superpage_size          INT;
+BEGIN
+    IF count_up_from_zero THEN
+        row_number := 0;
+    ELSE
+        row_number := -1;
+    END IF;
+
+    OPEN curs FOR EXECUTE query;
+
+    LOOP
+        FETCH curs INTO rec;
+        IF NOT FOUND THEN
+            IF result_row.pivot_point IS NOT NULL THEN
+                RETURN NEXT result_row;
+            END IF;
+            RETURN;
+        END IF;
+
+
+        -- Gather aggregate data based on the MBE row we're looking at now, authority axis
+        SELECT INTO all_arecords, result_row.sees, afields
+                ARRAY_AGG(DISTINCT abl.bib), -- bibs to check for visibility
+                ARRAY_TO_STRING(ARRAY_AGG(DISTINCT aal.source), $$,$$), -- authority record ids
+                ARRAY_AGG(DISTINCT map.metabib_field) -- authority-tag-linked CMF rows
+
+          FROM  metabib.browse_entry_simple_heading_map mbeshm
+                JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
+                JOIN authority.authority_linking aal ON ( ash.record = aal.source )
+                JOIN authority.bib_linking abl ON ( aal.target = abl.authority )
+                JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
+                    ash.atag = map.authority_field
+                    AND map.metabib_field = ANY(fields)
+                )
+          WHERE mbeshm.entry = rec.id;
+
+
+        -- Gather aggregate data based on the MBE row we're looking at now, bib axis
+        SELECT INTO all_brecords, result_row.authorities, bfields
+                ARRAY_AGG(DISTINCT source),
+                ARRAY_TO_STRING(ARRAY_AGG(DISTINCT authority), $$,$$),
+                ARRAY_AGG(DISTINCT def)
+          FROM  metabib.browse_entry_def_map
+          WHERE entry = rec.id
+                AND def = ANY(fields);
+
+        SELECT INTO result_row.fields ARRAY_TO_STRING(ARRAY_AGG(DISTINCT x), $$,$$) FROM UNNEST(afields || bfields) x;
+
+        result_row.sources := 0;
+        result_row.asources := 0;
+
+        -- Bib-linked vis checking
+        IF ARRAY_UPPER(all_brecords,1) IS NOT NULL THEN
+
+            full_end := ARRAY_LENGTH(all_brecords, 1);
+            superpage_size := COALESCE(browse_superpage_size, full_end);
+            slice_start := 1;
+            slice_end := superpage_size;
+
+            WHILE result_row.sources = 0 AND slice_start <= full_end LOOP
+                superpage_of_records := all_brecords[slice_start:slice_end];
+                qpfts_query :=
+                    'SELECT NULL::BIGINT AS id, ARRAY[r] AS records, ' ||
+                    '1::INT AS rel FROM (SELECT UNNEST(' ||
+                    quote_literal(superpage_of_records) || '::BIGINT[]) AS r) rr';
+
+                -- We use search.query_parser_fts() for visibility testing.
+                -- We're calling it once per browse-superpage worth of records
+                -- out of the set of records related to a given mbe, until we've
+                -- either exhausted that set of records or found at least 1
+                -- visible record.
+
+                SELECT INTO result_row.sources visible
+                    FROM search.query_parser_fts(
+                        context_org, NULL, qpfts_query, NULL,
+                        context_locations, 0, NULL, NULL, FALSE, staff, FALSE
+                    ) qpfts
+                    WHERE qpfts.rel IS NULL;
+
+                slice_start := slice_start + superpage_size;
+                slice_end := slice_end + superpage_size;
+            END LOOP;
+
+            -- Accurate?  Well, probably.
+            result_row.accurate := browse_superpage_size IS NULL OR
+                browse_superpage_size >= full_end;
+
+        END IF;
+
+        -- Authority-linked vis checking
+        IF ARRAY_UPPER(all_arecords,1) IS NOT NULL THEN
+
+            full_end := ARRAY_LENGTH(all_arecords, 1);
+            superpage_size := COALESCE(browse_superpage_size, full_end);
+            slice_start := 1;
+            slice_end := superpage_size;
+
+            WHILE result_row.asources = 0 AND slice_start <= full_end LOOP
+                superpage_of_records := all_arecords[slice_start:slice_end];
+                qpfts_query :=
+                    'SELECT NULL::BIGINT AS id, ARRAY[r] AS records, ' ||
+                    '1::INT AS rel FROM (SELECT UNNEST(' ||
+                    quote_literal(superpage_of_records) || '::BIGINT[]) AS r) rr';
+
+                -- We use search.query_parser_fts() for visibility testing.
+                -- We're calling it once per browse-superpage worth of records
+                -- out of the set of records related to a given mbe, via
+                -- authority until we've either exhausted that set of records
+                -- or found at least 1 visible record.
+
+                SELECT INTO result_row.asources visible
+                    FROM search.query_parser_fts(
+                        context_org, NULL, qpfts_query, NULL,
+                        context_locations, 0, NULL, NULL, FALSE, staff, FALSE
+                    ) qpfts
+                    WHERE qpfts.rel IS NULL;
+
+                slice_start := slice_start + superpage_size;
+                slice_end := slice_end + superpage_size;
+            END LOOP;
+
+
+            -- Accurate?  Well, probably.
+            result_row.aaccurate := browse_superpage_size IS NULL OR
+                browse_superpage_size >= full_end;
+
+        END IF;
+
+        IF result_row.sources > 0 OR result_row.asources > 0 THEN
+
+            -- The function that calls this function needs row_number in order
+            -- to correctly order results from two different runs of this
+            -- functions.
+            result_row.row_number := row_number;
+
+            -- Now, if row_counter is still less than limit, return a row.  If
+            -- not, but it is less than next_pivot_pos, continue on without
+            -- returning actual result rows until we find
+            -- that next pivot, and return it.
+
+            IF row_counter < result_limit THEN
+                result_row.browse_entry := rec.id;
+                result_row.value := rec.value;
+
+                RETURN NEXT result_row;
+            ELSE
+                result_row.browse_entry := NULL;
+                result_row.authorities := NULL;
+                result_row.fields := NULL;
+                result_row.value := NULL;
+                result_row.sources := NULL;
+                result_row.sees := NULL;
+                result_row.accurate := NULL;
+                result_row.aaccurate := NULL;
+                result_row.pivot_point := rec.id;
+
+                IF row_counter >= next_pivot_pos THEN
+                    RETURN NEXT result_row;
+                    RETURN;
+                END IF;
+            END IF;
+
+            IF count_up_from_zero THEN
+                row_number := row_number + 1;
+            ELSE
+                row_number := row_number - 1;
+            END IF;
+
+            -- row_counter is different from row_number.
+            -- It simply counts up from zero so that we know when
+            -- we've reached our limit.
+            row_counter := row_counter + 1;
+        END IF;
+    END LOOP;
+END;
+$p$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION metabib.browse(
+    search_field            INT[],
+    browse_term             TEXT,
+    context_org             INT DEFAULT NULL,
+    context_loc_group       INT DEFAULT NULL,
+    staff                   BOOL DEFAULT FALSE,
+    pivot_id                BIGINT DEFAULT NULL,
+    result_limit            INT DEFAULT 10
+) RETURNS SETOF metabib.flat_browse_entry_appearance AS $p$
+DECLARE
+    core_query              TEXT;
+    back_query              TEXT;
+    forward_query           TEXT;
+    pivot_sort_value        TEXT;
+    pivot_sort_fallback     TEXT;
+    context_locations       INT[];
+    browse_superpage_size   INT;
+    results_skipped         INT := 0;
+    back_limit              INT;
+    back_to_pivot           INT;
+    forward_limit           INT;
+    forward_to_pivot        INT;
+BEGIN
+    -- First, find the pivot if we were given a browse term but not a pivot.
+    IF pivot_id IS NULL THEN
+        pivot_id := metabib.browse_pivot(search_field, browse_term);
+    END IF;
+
+    SELECT INTO pivot_sort_value, pivot_sort_fallback
+        sort_value, value FROM metabib.browse_entry WHERE id = pivot_id;
+
+    -- Bail if we couldn't find a pivot.
+    IF pivot_sort_value IS NULL THEN
+        RETURN;
+    END IF;
+
+    -- Transform the context_loc_group argument (if any) (logc at the
+    -- TPAC layer) into a form we'll be able to use.
+    IF context_loc_group IS NOT NULL THEN
+        SELECT INTO context_locations ARRAY_AGG(location)
+            FROM asset.copy_location_group_map
+            WHERE lgroup = context_loc_group;
+    END IF;
+
+    -- Get the configured size of browse superpages.
+    SELECT INTO browse_superpage_size value     -- NULL ok
+        FROM config.global_flag
+        WHERE enabled AND name = 'opac.browse.holdings_visibility_test_limit';
+
+    -- First we're going to search backward from the pivot, then we're going
+    -- to search forward.  In each direction, we need two limits.  At the
+    -- lesser of the two limits, we delineate the edge of the result set
+    -- we're going to return.  At the greater of the two limits, we find the
+    -- pivot value that would represent an offset from the current pivot
+    -- at a distance of one "page" in either direction, where a "page" is a
+    -- result set of the size specified in the "result_limit" argument.
+    --
+    -- The two limits in each direction make four derived values in total,
+    -- and we calculate them now.
+    back_limit := CEIL(result_limit::FLOAT / 2);
+    back_to_pivot := result_limit;
+    forward_limit := result_limit / 2;
+    forward_to_pivot := result_limit - 1;
+
+    -- This is the meat of the SQL query that finds browse entries.  We'll
+    -- pass this to a function which uses it with a cursor, so that individual
+    -- rows may be fetched in a loop until some condition is satisfied, without
+    -- waiting for a result set of fixed size to be collected all at once.
+    core_query := '
+SELECT  mbe.id,
+        mbe.value,
+        mbe.sort_value
+  FROM  metabib.browse_entry mbe
+  WHERE (
+            EXISTS ( -- are there any bibs using this mbe via the requested fields?
+                SELECT  1
+                  FROM  metabib.browse_entry_def_map mbedm
+                  WHERE mbedm.entry = mbe.id AND mbedm.def = ANY(' || quote_literal(search_field) || ')
+                  LIMIT 1
+            ) OR EXISTS ( -- are there any authorities using this mbe via the requested fields?
+                SELECT  1
+                  FROM  metabib.browse_entry_simple_heading_map mbeshm
+                        JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
+                        JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
+                            ash.atag = map.authority_field
+                            AND map.metabib_field = ANY(' || quote_literal(search_field) || ')
+                        )
+                  WHERE mbeshm.entry = mbe.id
+            )
+        ) AND ';
+
+    -- This is the variant of the query for browsing backward.
+    back_query := core_query ||
+        ' mbe.sort_value <= ' || quote_literal(pivot_sort_value) ||
+    ' ORDER BY mbe.sort_value DESC, mbe.value DESC ';
+
+    -- This variant browses forward.
+    forward_query := core_query ||
+        ' mbe.sort_value > ' || quote_literal(pivot_sort_value) ||
+    ' ORDER BY mbe.sort_value, mbe.value ';
+
+    -- We now call the function which applies a cursor to the provided
+    -- queries, stopping at the appropriate limits and also giving us
+    -- the next page's pivot.
+    RETURN QUERY
+        SELECT * FROM metabib.staged_browse(
+            back_query, search_field, context_org, context_locations,
+            staff, browse_superpage_size, TRUE, back_limit, back_to_pivot
+        ) UNION
+        SELECT * FROM metabib.staged_browse(
+            forward_query, search_field, context_org, context_locations,
+            staff, browse_superpage_size, FALSE, forward_limit, forward_to_pivot
+        ) ORDER BY row_number DESC;
+
+END;
+$p$ LANGUAGE PLPGSQL;
+
+-- No 4XX inter-authority linking
+UPDATE authority.control_set_authority_field SET linking_subfield = NULL;
+UPDATE authority.control_set_authority_field SET linking_subfield = '0' WHERE tag LIKE ANY (ARRAY['5%','7%']);
+
+-- Map between authority controlled bib fields and stock indexing metabib fields
+INSERT INTO authority.control_set_bib_field_metabib_field_map (bib_field, metabib_field)
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '100' AND m.name = 'personal'
+
+        UNION
+
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '110' AND m.name = 'corporate'
+
+        UNION
+
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '111' AND m.name = 'conference'
+
+        UNION
+
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '130' AND m.name = 'uniform'
+
+        UNION
+
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '148' AND m.name = 'temporal'
+
+        UNION
+
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '150' AND m.name = 'topic'
+
+        UNION
+
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '151' AND m.name = 'geographic'
+
+        UNION
+
+    SELECT  DISTINCT b.id AS bib_field, m.id AS metabib_field
+      FROM  authority.control_set_bib_field b JOIN authority.control_set_authority_field a ON (b.authority_field = a.id), config.metabib_field m
+      WHERE a.tag = '155' AND m.name = 'genre' -- Just in case...
+;
+
+CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+DECLARE
+    ashs    authority.simple_heading%ROWTYPE;
+    mbe_row metabib.browse_entry%ROWTYPE;
+    mbe_id  BIGINT;
+    ash_id  BIGINT;
+BEGIN
+
+    IF NEW.deleted IS TRUE THEN -- If this authority is deleted
+        DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
+          -- Should remove matching $0 from controlled fields at the same time?
+
+        -- XXX What do we about the actual linking subfields present in
+        -- authority records that target this one when this happens?
+        DELETE FROM authority.authority_linking
+            WHERE source = NEW.id OR target = NEW.id;
+
+        RETURN NEW; -- and we're done
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+
+        -- Propagate these updates to any linked bib records
+        PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
+
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
+        DELETE FROM authority.authority_linking WHERE source = NEW.id;
+    END IF;
+
+    INSERT INTO authority.authority_linking (source, target, field)
+        SELECT source, target, field FROM authority.calculate_authority_linking(
+            NEW.id, NEW.control_set, NEW.marc::XML
+        );
+
+    FOR ashs IN SELECT * FROM authority.simple_heading_set(NEW.marc) LOOP
+
+        INSERT INTO authority.simple_heading (record,atag,value,sort_value)
+            VALUES (ashs.record, ashs.atag, ashs.value, ashs.sort_value);
+        ash_id := CURRVAL('authority.simple_heading_id_seq'::REGCLASS);
+
+        SELECT INTO mbe_row * FROM metabib.browse_entry
+            WHERE value = ashs.value AND sort_value = ashs.sort_value;
+
+        IF FOUND THEN
+            mbe_id := mbe_row.id;
+        ELSE
+            INSERT INTO metabib.browse_entry
+                ( value, sort_value ) VALUES
+                ( ashs.value, ashs.sort_value );
+
+            mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
+        END IF;
+
+        INSERT INTO metabib.browse_entry_simple_heading_map (entry,simple_heading) VALUES (mbe_id,ash_id);
+
+    END LOOP;
+
+    -- Flatten and insert the afr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM authority.reingest_authority_full_rec(NEW.id);
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
+        END IF;
+    END IF;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0838', :eg_version);
+
+DELETE FROM config.metabib_field_index_norm_map
+    WHERE field = 25 AND norm IN (
+        SELECT id
+        FROM config.index_normalizer
+        WHERE func IN ('search_normalize','split_date_range')
+    );
+
+\qecho If your site's bibcn searches are affected by this issue, you may wish
+\qecho to reingest your bib records now.  It's probably not worth it for many
+\qecho sites.
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0839', :eg_version);
+
+UPDATE config.metabib_field
+SET
+    xpath = $$//mods32:mods/mods32:titleInfo[mods32:title and starts-with(@type,'alternative')]$$,
+    browse_sort_xpath = $$*[local-name() != "nonSort"]$$,
+    browse_xpath = NULL
+WHERE
+    field_class = 'title' AND name = 'alternative' ;
+
+-- The following function only appears in the upgrade script and not the
+-- baseline schema because it's not necessary in the latter (and it's a
+-- temporary function).  It just serves to do a hopefully cheaper, more
+-- focused reingest just to hit the alternative title index.
+
+-- This cribs from the guts of metabib.reingest_metabib_field_entries(),
+-- and if it actually is a timesaver over a full reingest, then at some
+-- point in the future it would be nice if we broke it out into a separate
+-- function to make things like this easier.
+
+CREATE OR REPLACE FUNCTION pg_temp.alternative_title_reingest( bib_id BIGINT ) RETURNS VOID AS $func$
+DECLARE
+    ind_data        metabib.field_entry_template%ROWTYPE;
+    mbe_row         metabib.browse_entry%ROWTYPE;
+    mbe_id          BIGINT;
+    b_skip_facet    BOOL := false;
+    b_skip_browse   BOOL := false;
+    b_skip_search   BOOL := false;
+    alt_title       INT;
+    value_prepped   TEXT;
+BEGIN
+    SELECT INTO alt_title id FROM config.metabib_field WHERE field_class = 'title' AND name = 'alternative';
+    FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) WHERE field = alt_title LOOP
+        IF ind_data.field < 0 THEN
+            ind_data.field = -1 * ind_data.field;
+        END IF;
+
+        IF ind_data.facet_field AND NOT b_skip_facet THEN
+            INSERT INTO metabib.facet_entry (field, source, value)
+                VALUES (ind_data.field, ind_data.source, ind_data.value);
+        END IF;
+
+        IF ind_data.browse_field AND NOT b_skip_browse THEN
+            -- A caveat about this SELECT: this should take care of replacing
+            -- old mbe rows when data changes, but not if normalization (by
+            -- which I mean specifically the output of
+            -- evergreen.oils_tsearch2()) changes.  It may or may not be
+            -- expensive to add a comparison of index_vector to index_vector
+            -- to the WHERE clause below.
+
+            value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
+            SELECT INTO mbe_row * FROM metabib.browse_entry
+                WHERE value = value_prepped AND sort_value = ind_data.sort_value;
+
+            IF FOUND THEN
+                mbe_id := mbe_row.id;
+            ELSE
+                INSERT INTO metabib.browse_entry
+                    ( value, sort_value ) VALUES
+                    ( value_prepped, ind_data.sort_value );
+
+                mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
+            END IF;
+
+            INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
+                VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
+        END IF;
+
+        -- Avoid inserting duplicate rows, but retain granularity of being
+        -- able to search browse fields with "starts with" type operators
+        -- (for example, for titles of songs in music albums)
+        IF (ind_data.search_field OR ind_data.browse_field) AND NOT b_skip_search THEN
+            EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
+                '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
+                INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
+                -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
+            IF mbe_id IS NULL THEN
+                EXECUTE $$
+                INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
+                    VALUES ($$ ||
+                        quote_literal(ind_data.field) || $$, $$ ||
+                        quote_literal(ind_data.source) || $$, $$ ||
+                        quote_literal(ind_data.value) ||
+                    $$);$$;
+            END IF;
+        END IF;
+
+    END LOOP;
+
+    IF NOT b_skip_search THEN
+        PERFORM metabib.update_combined_index_vectors(bib_id);
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0840', :eg_version);
+
+UPDATE config.usr_setting_type SET 
+    grp = 'gui',
+    opac_visible = FALSE,
+    label = 
+    oils_i18n_gettext(
+        'ui.grid_columns.conify.config.circ_matrix_matchpoint',
+        'Circulation Policy Configuration',
+        'cust',
+        'label'
+    ),
+    description = oils_i18n_gettext(
+        'ui.grid_columns.conify.config.circ_matrix_matchpoint',
+        'Circulation Policy Configuration Column Settings',
+        'cust',
+        'description'
+    ),
+    datatype = 'string'
+    WHERE name = 'ui.grid_columns.conify.config.circ_matrix_matchpoint'
+;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0841', :eg_version);
+
+ALTER TABLE config.metabib_field_ts_map DROP CONSTRAINT metabib_field_ts_map_metabib_field_fkey;
+ALTER TABLE config.metabib_search_alias DROP CONSTRAINT metabib_search_alias_field_fkey;
+ALTER TABLE config.z3950_index_field_map DROP CONSTRAINT z3950_index_field_map_metabib_field_fkey;
+ALTER TABLE metabib.browse_entry_def_map DROP CONSTRAINT browse_entry_def_map_def_fkey;
+
+ALTER TABLE config.metabib_field_ts_map ADD CONSTRAINT metabib_field_ts_map_metabib_field_fkey FOREIGN KEY (metabib_field) REFERENCES config.metabib_field(id) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE config.metabib_search_alias ADD CONSTRAINT metabib_search_alias_field_fkey FOREIGN KEY (field) REFERENCES config.metabib_field(id) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE config.z3950_index_field_map ADD CONSTRAINT z3950_index_field_map_metabib_field_fkey FOREIGN KEY (metabib_field) REFERENCES config.metabib_field(id) DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE metabib.browse_entry_def_map ADD CONSTRAINT browse_entry_def_map_def_fkey FOREIGN KEY (def) REFERENCES config.metabib_field(id) DEFERRABLE INITIALLY DEFERRED;
+
+
+DROP FUNCTION IF EXISTS config.modify_metabib_field(source INT, target INT);
+CREATE FUNCTION config.modify_metabib_field(v_source INT, target INT) RETURNS INT AS $func$
+DECLARE
+    f_class TEXT;
+    check_id INT;
+    target_id INT;
+BEGIN
+    SELECT field_class INTO f_class FROM config.metabib_field WHERE id = v_source;
+    IF NOT FOUND THEN
+        RETURN 0;
+    END IF;
+    IF target IS NULL THEN
+        target_id = v_source + 1000;
+    ELSE
+        target_id = target;
+    END IF;
+    SELECT id FROM config.metabib_field INTO check_id WHERE id = target_id;
+    IF FOUND THEN
+        RAISE NOTICE 'Cannot bump config.metabib_field.id from % to %; the target ID already exists.', v_source, target_id;
+        RETURN 0;
+    END IF;
+    UPDATE config.metabib_field SET id = target_id WHERE id = v_source;
+    EXECUTE ' UPDATE metabib.' || f_class || '_field_entry SET field = ' || target_id || ' WHERE field = ' || v_source;
+    UPDATE config.metabib_field_ts_map SET metabib_field = target_id WHERE metabib_field = v_source;
+    UPDATE config.metabib_field_index_norm_map SET field = target_id WHERE field = v_source;
+    UPDATE search.relevance_adjustment SET field = target_id WHERE field = v_source;
+    UPDATE config.metabib_search_alias SET field = target_id WHERE field = v_source;
+    UPDATE config.z3950_index_field_map SET metabib_field = target_id WHERE metabib_field = v_source;
+    UPDATE metabib.browse_entry_def_map SET def = target_id WHERE def = v_source;
+    RETURN 1;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+SELECT config.modify_metabib_field(id, NULL)
+    FROM config.metabib_field
+    WHERE id > 30;
+
+SELECT SETVAL('config.metabib_field_id_seq', GREATEST(1000, (SELECT MAX(id) FROM config.metabib_field)));
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0842', :eg_version);
+
+-- this upgrade is only for people coming from 2_3, and is a NO-OP for those on 2_4
+ALTER TABLE config.metabib_field_ts_map DROP CONSTRAINT metabib_field_ts_map_metabib_field_fkey;
+
+ALTER TABLE config.metabib_field_ts_map ADD CONSTRAINT metabib_field_ts_map_metabib_field_fkey FOREIGN KEY (metabib_field) REFERENCES config.metabib_field(id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0843', :eg_version);
+
+-- this upgrade file serves 2 purposes:
+-- 1) add ON UPDATE CASCADE for those upgrading 2_5/master
+-- 2) alter config.z3950_index_field_map for those upgrading from 2_4 and previous (other lines
+--    are no-ops in this case)
+ALTER TABLE config.metabib_search_alias DROP CONSTRAINT metabib_search_alias_field_fkey;
+ALTER TABLE config.z3950_index_field_map DROP CONSTRAINT z3950_index_field_map_metabib_field_fkey;
+ALTER TABLE metabib.browse_entry_def_map DROP CONSTRAINT browse_entry_def_map_def_fkey;
+
+ALTER TABLE config.metabib_search_alias ADD CONSTRAINT metabib_search_alias_field_fkey FOREIGN KEY (field) REFERENCES config.metabib_field(id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE config.z3950_index_field_map ADD CONSTRAINT z3950_index_field_map_metabib_field_fkey FOREIGN KEY (metabib_field) REFERENCES config.metabib_field(id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE metabib.browse_entry_def_map ADD CONSTRAINT browse_entry_def_map_def_fkey FOREIGN KEY (def) REFERENCES config.metabib_field(id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0844', :eg_version);
+
+-- 953.data.MODS32-xsl.sql
+UPDATE config.xml_transform SET xslt=$$<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns="http://www.loc.gov/mods/v3" xmlns:marc="http://www.loc.gov/MARC21/slim" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xlink marc" version="1.0">
+       <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
+<!--
+Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements 
+  with corresponding @type and @invalid eq 'yes' when subfields z or y (in the case of 022) exist in the MARCXML ::: 2007/01/04 17:35:20 cred
+
+Revision 1.13 - Changed order of output under cartographics to reflect schema  2006/11/28 tmee
+       
+Revision 1.12 - Updated to reflect MODS 3.2 Mapping  2006/10/11 tmee
+               
+Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>
+      2006/04/08  jrad
+
+Revision 1.10 MODS 3.1 revisions to language and classification elements  
+                               (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)
+                               2006/02/06  ggar
+
+Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
+
+Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
+
+Revision 1.7 2004/03/25 08:29 jrad
+
+Revision 1.6 various validation fixes 2004/02/20 ntra
+
+Revision 1.5  2003/10/02 16:18:58  ntra
+MODS2 to MODS3 updates, language unstacking and 
+de-duping, chopPunctuation expanded
+
+Revision 1.3  2003/04/03 00:07:19  ntra
+Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
+
+Revision 1.2  2003/03/24 19:37:42  ckeith
+Added Log Comment
+
+-->
+       <xsl:template match="/">
+               <xsl:choose>
+                       <xsl:when test="//marc:collection">
+                               <modsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
+                                       <xsl:for-each select="//marc:collection/marc:record">
+                                               <mods version="3.2">
+                                                       <xsl:call-template name="marcRecord"/>
+                                               </mods>
+                                       </xsl:for-each>
+                               </modsCollection>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
+                                       <xsl:for-each select="//marc:record">
+                                               <xsl:call-template name="marcRecord"/>
+                                       </xsl:for-each>
+                               </mods>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="marcRecord">
+               <xsl:variable name="leader" select="marc:leader"/>
+               <xsl:variable name="leader6" select="substring($leader,7,1)"/>
+               <xsl:variable name="leader7" select="substring($leader,8,1)"/>
+               <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
+               <xsl:variable name="typeOf008">
+                       <xsl:choose>
+                               <xsl:when test="$leader6='a'">
+                                       <xsl:choose>
+                                               <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
+                                               <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
+                                       </xsl:choose>
+                               </xsl:when>
+                               <xsl:when test="$leader6='t'">BK</xsl:when>
+                               <xsl:when test="$leader6='p'">MM</xsl:when>
+                               <xsl:when test="$leader6='m'">CF</xsl:when>
+                               <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
+                               <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
+                               <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'">MU</xsl:when>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:for-each select="marc:datafield[@tag='245']">
+                       <titleInfo>
+                               <xsl:variable name="title">
+                                       <xsl:choose>
+                                               <xsl:when test="marc:subfield[@code='b']">
+                                                       <xsl:call-template name="specialSubfieldSelect">
+                                                               <xsl:with-param name="axis">b</xsl:with-param>
+                                                               <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">abfgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                               </xsl:variable>
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="$title"/>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <xsl:choose>
+                                       <xsl:when test="@ind2>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop"/>
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:if test="marc:subfield[@code='b']">
+                                       <subTitle>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="axis">b</xsl:with-param>
+                                                                       <xsl:with-param name="anyCodes">b</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">afgk</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </subTitle>
+                               </xsl:if>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleInfo>
+                       <!-- A form of title that ignores non-filing characters; useful
+                                for not converting "L'Oreal" into "L' Oreal" at index time -->
+                       <titleNonfiling>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">abfgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleNonfiling>
+                       <!-- hybrid of titleInfo and titleNonfiling which will give us a preformatted string (for punctuation)
+                                but also keep the nonSort stuff in a separate field (for sorting) -->
+                       <titleBrowse>
+                               <xsl:variable name="titleBrowseChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">abfgk</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <xsl:choose>
+                                       <xsl:when test="@ind2>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleBrowseChop,1,@ind2)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleBrowseChop,@ind2+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleBrowseChop"/>
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleBrowse>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='210']">
+                       <titleInfo type="abbreviated">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">a</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="subtitle"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='242']">
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <!-- 1/04 removed $h, b -->
+                                                       <xsl:with-param name="codes">a</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo type="translated">
+                               <!--09/01/04 Added subfield $y-->
+                               <xsl:for-each select="marc:subfield[@code='y']">
+                                       <xsl:attribute name="lang">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <title>
+                                       <xsl:value-of select="$titleChop" />
+                               </title>
+                               <!-- 1/04 fix -->
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+                       <titleInfo type="translated-nfi">
+                               <xsl:for-each select="marc:subfield[@code='y']">
+                                       <xsl:attribute name="lang">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <xsl:choose>
+                                       <xsl:when test="@ind2>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop" />
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='246']">
+                       <titleInfo type="alternative">
+                               <xsl:for-each select="marc:subfield[@code='i']">
+                                       <xsl:attribute name="displayLabel">
+                                               <xsl:value-of select="text()"/>
+                                       </xsl:attribute>
+                               </xsl:for-each>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <!-- 1/04 removed $h, $b -->
+                                                               <xsl:with-param name="codes">af</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:call-template name="subtitle"/>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
+                       <xsl:variable name="nfi">
+                               <xsl:choose>
+                                       <xsl:when test="@tag='240'">
+                                               <xsl:value-of select="@ind2"/>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:value-of select="@ind1"/>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </xsl:variable>
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="uri" />
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield">
+                                               <xsl:if test="(contains('adfklmor',@code) and (not(../marc:subfield[@code='n' or @code='p']) or (following-sibling::marc:subfield[@code='n' or @code='p'])))">
+                                                       <xsl:value-of select="text()"/>
+                                                       <xsl:text> </xsl:text>
+                                               </xsl:if>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo type="uniform">
+                               <title>
+                                       <xsl:value-of select="$titleChop"/>
+                               </title>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+                       <titleInfo type="uniform-nfi">
+                               <xsl:choose>
+                                       <xsl:when test="$nfi>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,$nfi)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,$nfi+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop"/>
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">ah</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo type="alternative">
+                               <title>
+                                       <xsl:value-of select="$titleChop" />
+                               </title>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+                       <titleInfo type="alternative-nfi">
+                               <xsl:choose>
+                                       <xsl:when test="@ind1>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind1)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind1+1)"/>
+                                               </title>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop" />
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="part"/>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='100']">
+                       <name type="personal">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDQ"/>
+                               <xsl:call-template name="affiliation"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='110']">
+                       <name type="corporate">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDN"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='111']">
+                       <name type="conference">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameACDEQ"/>
+                               <role>
+                                       <roleTerm authority="marcrelator" type="text">creator</roleTerm>
+                               </role>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
+                       <name type="personal">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDQ"/>
+                               <xsl:call-template name="affiliation"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
+                       <name type="corporate">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameABCDN"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
+                       <name type="conference">
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="nameACDEQ"/>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
+                       <name>
+                               <xsl:if test="@ind1=1">
+                                       <xsl:attribute name="type">
+                                               <xsl:text>personal</xsl:text>
+                                       </xsl:attribute>
+                               </xsl:if>
+                               <namePart>
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </namePart>
+                               <xsl:call-template name="role"/>
+                       </name>
+               </xsl:for-each>
+               <typeOfResource>
+                       <xsl:if test="$leader7='c'">
+                               <xsl:attribute name="collection">yes</xsl:attribute>
+                       </xsl:if>
+                       <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
+                               <xsl:attribute name="manuscript">yes</xsl:attribute>
+                       </xsl:if>
+                       <xsl:choose>
+                               <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
+                               <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
+                               <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
+                               <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
+                               <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
+                               <xsl:when test="$leader6='k'">still image</xsl:when>
+                               <xsl:when test="$leader6='g'">moving image</xsl:when>
+                               <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
+                               <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
+                               <xsl:when test="$leader6='p'">mixed material</xsl:when>
+                       </xsl:choose>
+               </typeOfResource>
+               <xsl:if test="substring($controlField008,26,1)='d'">
+                       <genre authority="marc">globe</genre>
+               </xsl:if>
+               <xsl:if test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
+                       <genre authority="marc">remote sensing image</genre>
+               </xsl:if>
+               <xsl:if test="$typeOf008='MP'">
+                       <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-25='a' or $controlField008-25='b' or $controlField008-25='c' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
+                                       <genre authority="marc">map</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
+                                       <genre authority="marc">atlas</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='SE'">
+                       <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-21='d'">
+                                       <genre authority="marc">database</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='l'">
+                                       <genre authority="marc">loose-leaf</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='m'">
+                                       <genre authority="marc">series</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='n'">
+                                       <genre authority="marc">newspaper</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='p'">
+                                       <genre authority="marc">periodical</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-21='w'">
+                                       <genre authority="marc">web site</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
+                       <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="contains($controlField008-24,'a')">
+                                       <genre authority="marc">abstract or summary</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'b')">
+                                       <genre authority="marc">bibliography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'c')">
+                                       <genre authority="marc">catalog</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'d')">
+                                       <genre authority="marc">dictionary</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'e')">
+                                       <genre authority="marc">encyclopedia</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'f')">
+                                       <genre authority="marc">handbook</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'g')">
+                                       <genre authority="marc">legal article</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'i')">
+                                       <genre authority="marc">index</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'k')">
+                                       <genre authority="marc">discography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'l')">
+                                       <genre authority="marc">legislation</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'m')">
+                                       <genre authority="marc">theses</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'n')">
+                                       <genre authority="marc">survey of literature</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'o')">
+                                       <genre authority="marc">review</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'p')">
+                                       <genre authority="marc">programmed text</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'q')">
+                                       <genre authority="marc">filmography</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'r')">
+                                       <genre authority="marc">directory</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'s')">
+                                       <genre authority="marc">statistics</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'t')">
+                                       <genre authority="marc">technical report</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'v')">
+                                       <genre authority="marc">legal case and case notes</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'w')">
+                                       <genre authority="marc">law report or digest</genre>
+                               </xsl:when>
+                               <xsl:when test="contains($controlField008-24,'z')">
+                                       <genre authority="marc">treaty</genre>
+                               </xsl:when>
+                       </xsl:choose>
+                       <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-29='1'">
+                                       <genre authority="marc">conference publication</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='CF'">
+                       <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-26='a'">
+                                       <genre authority="marc">numeric data</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='e'">
+                                       <genre authority="marc">database</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='f'">
+                                       <genre authority="marc">font</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-26='g'">
+                                       <genre authority="marc">game</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='BK'">
+                       <xsl:if test="substring($controlField008,25,1)='j'">
+                               <genre authority="marc">patent</genre>
+                       </xsl:if>
+                       <xsl:if test="substring($controlField008,31,1)='1'">
+                               <genre authority="marc">festschrift</genre>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"></xsl:variable>
+                       <xsl:if test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
+                               <genre authority="marc">biography</genre>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-33='e'">
+                                       <genre authority="marc">essay</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='d'">
+                                       <genre authority="marc">drama</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='c'">
+                                       <genre authority="marc">comic strip</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='l'">
+                                       <genre authority="marc">fiction</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='h'">
+                                       <genre authority="marc">humor, satire</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='i'">
+                                       <genre authority="marc">letter</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='f'">
+                                       <genre authority="marc">novel</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='j'">
+                                       <genre authority="marc">short story</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='s'">
+                                       <genre authority="marc">speech</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:if test="$typeOf008='MU'">
+                       <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"></xsl:variable>
+                       <xsl:if test="contains($controlField008-30-31,'b')">
+                               <genre authority="marc">biography</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'c')">
+                               <genre authority="marc">conference publication</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'d')">
+                               <genre authority="marc">drama</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'e')">
+                               <genre authority="marc">essay</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'f')">
+                               <genre authority="marc">fiction</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'o')">
+                               <genre authority="marc">folktale</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'h')">
+                               <genre authority="marc">history</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'k')">
+                               <genre authority="marc">humor, satire</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'m')">
+                               <genre authority="marc">memoir</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'p')">
+                               <genre authority="marc">poetry</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'r')">
+                               <genre authority="marc">rehearsal</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'g')">
+                               <genre authority="marc">reporting</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'s')">
+                               <genre authority="marc">sound</genre>
+                       </xsl:if>
+                       <xsl:if test="contains($controlField008-30-31,'l')">
+                               <genre authority="marc">speech</genre>
+                       </xsl:if>
+               </xsl:if>
+               <xsl:if test="$typeOf008='VM'">
+                       <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"></xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="$controlField008-33='a'">
+                                       <genre authority="marc">art original</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='b'">
+                                       <genre authority="marc">kit</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='c'">
+                                       <genre authority="marc">art reproduction</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='d'">
+                                       <genre authority="marc">diorama</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='f'">
+                                       <genre authority="marc">filmstrip</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='g'">
+                                       <genre authority="marc">legal article</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='i'">
+                                       <genre authority="marc">picture</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='k'">
+                                       <genre authority="marc">graphic</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='l'">
+                                       <genre authority="marc">technical drawing</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='m'">
+                                       <genre authority="marc">motion picture</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='n'">
+                                       <genre authority="marc">chart</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='o'">
+                                       <genre authority="marc">flash card</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='p'">
+                                       <genre authority="marc">microscope slide</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
+                                       <genre authority="marc">model</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='r'">
+                                       <genre authority="marc">realia</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='s'">
+                                       <genre authority="marc">slide</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='t'">
+                                       <genre authority="marc">transparency</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='v'">
+                                       <genre authority="marc">videorecording</genre>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-33='w'">
+                                       <genre authority="marc">toy</genre>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=655]">
+                       <genre authority="marc">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"/>
+                               </xsl:attribute>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abvxyz</xsl:with-param>
+                                       <xsl:with-param name="delimeter">-</xsl:with-param>
+                               </xsl:call-template>
+                       </genre>
+               </xsl:for-each>
+               <originInfo>
+                       <xsl:variable name="MARCpublicationCode" select="normalize-space(substring($controlField008,16,3))"></xsl:variable>
+                       <xsl:if test="translate($MARCpublicationCode,'|','')">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">code</xsl:attribute>
+                                               <xsl:attribute name="authority">marccountry</xsl:attribute>
+                                               <xsl:value-of select="$MARCpublicationCode"/>
+                                       </placeTerm>
+                               </place>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">code</xsl:attribute>
+                                               <xsl:attribute name="authority">iso3166</xsl:attribute>
+                                               <xsl:value-of select="."/>
+                                       </placeTerm>
+                               </place>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
+                               <place>
+                                       <placeTerm>
+                                               <xsl:attribute name="type">text</xsl:attribute>
+                                               <xsl:call-template name="chopPunctuationFront">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="chopPunctuation">
+                                                                       <xsl:with-param name="chopString" select="."/>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </placeTerm>
+                               </place>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
+                               <dateValid point="start">
+                                       <xsl:value-of select="."/>
+                               </dateValid>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
+                               <dateValid point="end">
+                                       <xsl:value-of select="."/>
+                               </dateValid>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
+                               <dateModified>
+                                       <xsl:value-of select="."/>
+                               </dateModified>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
+                               <xsl:choose>
+                                       <xsl:when test="@code='b'">
+                                               <publisher>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                               <xsl:with-param name="punctuation">
+                                                                       <xsl:text>:,;/ </xsl:text>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </publisher>
+                                       </xsl:when>
+                                       <xsl:when test="@code='c'">
+                                               <dateIssued>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."/>
+                                                       </xsl:call-template>
+                                               </dateIssued>
+                                       </xsl:when>
+                                       <xsl:when test="@code='g'">
+                                               <dateCreated>
+                                                       <xsl:value-of select="."/>
+                                               </dateCreated>
+                                       </xsl:when>
+                               </xsl:choose>
+                       </xsl:for-each>
+                       <xsl:variable name="dataField260c">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="marc:datafield[@tag=260]/marc:subfield[@code='c']"></xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <xsl:variable name="controlField008-7-10" select="normalize-space(substring($controlField008, 8, 4))"></xsl:variable>
+                       <xsl:variable name="controlField008-11-14" select="normalize-space(substring($controlField008, 12, 4))"></xsl:variable>
+                       <xsl:variable name="controlField008-6" select="normalize-space(substring($controlField008, 7, 1))"></xsl:variable>
+                       <xsl:if test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
+                               <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
+                                       <dateIssued encoding="marc">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
+                               <xsl:if test="$controlField008-7-10">
+                                       <dateIssued encoding="marc" point="start">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <dateIssued encoding="marc" point="end">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='q'">
+                               <xsl:if test="$controlField008-7-10">
+                                       <dateIssued encoding="marc" point="start" qualifier="questionable">
+                                               <xsl:value-of select="$controlField008-7-10"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='q'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <dateIssued encoding="marc" point="end" qualifier="questionable">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </dateIssued>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:if test="$controlField008-6='t'">
+                               <xsl:if test="$controlField008-11-14">
+                                       <copyrightDate encoding="marc">
+                                               <xsl:value-of select="$controlField008-11-14"/>
+                                       </copyrightDate>
+                               </xsl:if>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
+                               <dateCaptured encoding="iso8601">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
+                               <dateCaptured encoding="iso8601" point="start">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
+                               <dateCaptured encoding="iso8601" point="end">
+                                       <xsl:value-of select="."/>
+                               </dateCaptured>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
+                               <edition>
+                                       <xsl:value-of select="."/>
+                               </edition>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:leader">
+                               <issuance>
+                                       <xsl:choose>
+                                               <xsl:when test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">monographic</xsl:when>
+                                               <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">continuing</xsl:when>
+                                       </xsl:choose>
+                               </issuance>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
+                               <frequency>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">ab</xsl:with-param>
+                                       </xsl:call-template>
+                               </frequency>
+                       </xsl:for-each>
+               </originInfo>
+               <xsl:variable name="controlField008-35-37" select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"></xsl:variable>
+               <xsl:if test="$controlField008-35-37">
+                       <language>
+                               <languageTerm authority="iso639-2b" type="code">
+                                       <xsl:value-of select="substring($controlField008,36,3)"/>
+                               </languageTerm>
+                       </language>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=041]">
+                       <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='d' or @code='e' or @code='f' or @code='g' or @code='h']">
+                               <xsl:variable name="langCodes" select="."/>
+                               <xsl:choose>
+                                       <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
+                                               <!-- not stacked but could be repeated -->
+                                               <xsl:call-template name="rfcLanguages">
+                                                       <xsl:with-param name="nodeNum">
+                                                               <xsl:value-of select="1"/>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="usedLanguages">
+                                                               <xsl:text></xsl:text>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="controlField008-35-37">
+                                                               <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <!-- iso -->
+                                               <xsl:variable name="allLanguages">
+                                                       <xsl:copy-of select="$langCodes"></xsl:copy-of>
+                                               </xsl:variable>
+                                               <xsl:variable name="currentLanguage">
+                                                       <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
+                                               </xsl:variable>
+                                               <xsl:call-template name="isoLanguage">
+                                                       <xsl:with-param name="currentLanguage">
+                                                               <xsl:value-of select="substring($allLanguages,1,3)"></xsl:value-of>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="remainingLanguages">
+                                                               <xsl:value-of select="substring($allLanguages,4,string-length($allLanguages)-3)"></xsl:value-of>
+                                                       </xsl:with-param>
+                                                       <xsl:with-param name="usedLanguages">
+                                                               <xsl:if test="$controlField008-35-37">
+                                                                       <xsl:value-of select="$controlField008-35-37"></xsl:value-of>
+                                                               </xsl:if>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </xsl:for-each>
+               </xsl:for-each>
+               <xsl:variable name="physicalDescription">
+                       <!--3.2 change tmee 007/11 -->
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
+                               <digitalOrigin>reformatted digital</digitalOrigin>
+                       </xsl:if>
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
+                               <digitalOrigin>digitized microfilm</digitalOrigin>
+                       </xsl:if>
+                       <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
+                               <digitalOrigin>digitized other analog</digitalOrigin>
+                       </xsl:if>
+                       <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"></xsl:variable>
+                       <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"></xsl:variable>
+                       <xsl:variable name="check008-23">
+                               <xsl:if test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
+                                       <xsl:value-of select="true()"></xsl:value-of>
+                               </xsl:if>
+                       </xsl:variable>
+                       <xsl:variable name="check008-29">
+                               <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
+                                       <xsl:value-of select="true()"></xsl:value-of>
+                               </xsl:if>
+                       </xsl:variable>
+                       <xsl:choose>
+                               <xsl:when test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
+                                       <form authority="marcform">braille</form>
+                               </xsl:when>
+                               <xsl:when test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
+                                       <form authority="marcform">print</form>
+                               </xsl:when>
+                               <xsl:when test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
+                                       <form authority="marcform">electronic</form>
+                               </xsl:when>
+                               <xsl:when test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
+                                       <form authority="marcform">microfiche</form>
+                               </xsl:when>
+                               <xsl:when test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
+                                       <form authority="marcform">microfilm</form>
+                               </xsl:when>
+                       </xsl:choose>
+                       <!-- 1/04 fix -->
+                       <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
+                               <form authority="gmd">
+                                       <xsl:call-template name="chopBrackets">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </form>
+                       </xsl:if>
+                       <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
+                               <form>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </form>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
+                               <xsl:choose>
+                                       <xsl:when test="substring(text(),14,1)='a'">
+                                               <reformattingQuality>access</reformattingQuality>
+                                       </xsl:when>
+                                       <xsl:when test="substring(text(),14,1)='p'">
+                                               <reformattingQuality>preservation</reformattingQuality>
+                                       </xsl:when>
+                                       <xsl:when test="substring(text(),14,1)='r'">
+                                               <reformattingQuality>replacement</reformattingQuality>
+                                       </xsl:when>
+                               </xsl:choose>
+                       </xsl:for-each>
+                       <!--3.2 change tmee 007/01 -->
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
+                               <form authority="smd">chip cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
+                               <form authority="smd">computer optical disc cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
+                               <form authority="smd">magnetic disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
+                               <form authority="smd">magneto-optical disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
+                               <form authority="smd">optical disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
+                               <form authority="smd">tape cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
+                               <form authority="smd">tape cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
+                               <form authority="smd">tape reel</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
+                               <form authority="smd">celestial globe</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
+                               <form authority="smd">earth moon globe</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
+                               <form authority="smd">planetary or lunar globe</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
+                               <form authority="smd">terrestrial globe</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
+                               <form authority="smd">kit</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
+                               <form authority="smd">atlas</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
+                               <form authority="smd">diagram</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
+                               <form authority="smd">map</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
+                               <form authority="smd">model</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
+                               <form authority="smd">profile</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote-sensing image</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
+                               <form authority="smd">section</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
+                               <form authority="smd">view</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
+                               <form authority="smd">aperture card</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
+                               <form authority="smd">microfiche</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
+                               <form authority="smd">microfiche cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
+                               <form authority="smd">microfilm cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
+                               <form authority="smd">microfilm cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
+                               <form authority="smd">microfilm reel</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
+                               <form authority="smd">microopaque</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
+                               <form authority="smd">film cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
+                               <form authority="smd">film cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
+                               <form authority="smd">film reel</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
+                               <form authority="smd">chart</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
+                               <form authority="smd">collage</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
+                               <form authority="smd">drawing</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
+                               <form authority="smd">flash card</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
+                               <form authority="smd">painting</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
+                               <form authority="smd">photomechanical print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
+                               <form authority="smd">photonegative</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
+                               <form authority="smd">photoprint</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
+                               <form authority="smd">picture</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
+                               <form authority="smd">print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
+                               <form authority="smd">technical drawing</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
+                               <form authority="smd">notated music</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
+                               <form authority="smd">filmslip</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
+                               <form authority="smd">filmstrip cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
+                               <form authority="smd">filmstrip roll</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
+                               <form authority="smd">other filmstrip type</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
+                               <form authority="smd">slide</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
+                               <form authority="smd">transparency</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
+                               <form authority="smd">remote-sensing image</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
+                               <form authority="smd">cylinder</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
+                               <form authority="smd">roll</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
+                               <form authority="smd">sound cartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
+                               <form authority="smd">sound cassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
+                               <form authority="smd">sound disc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
+                               <form authority="smd">sound-tape reel</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
+                               <form authority="smd">sound-track film</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
+                               <form authority="smd">wire recording</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
+                               <form authority="smd">braille</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
+                               <form authority="smd">combination</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
+                               <form authority="smd">moon</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
+                               <form authority="smd">tactile, with no writing system</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
+                               <form authority="smd">braille</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
+                               <form authority="smd">large print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
+                               <form authority="smd">regular print</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
+                               <form authority="smd">text in looseleaf binder</form>
+                       </xsl:if>
+                       
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
+                               <form authority="smd">videocartridge</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
+                               <form authority="smd">videocassette</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
+                               <form authority="smd">videodisc</form>
+                       </xsl:if>
+                       <xsl:if test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
+                               <form authority="smd">videoreel</form>
+                       </xsl:if>
+                       
+                       <xsl:for-each select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)>1]">
+                               <internetMediaType>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </internetMediaType>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=300]">
+                               <extent>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abce</xsl:with-param>
+                                       </xsl:call-template>
+                               </extent>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($physicalDescription))">
+                       <physicalDescription>
+                               <xsl:copy-of select="$physicalDescription"></xsl:copy-of>
+                       </physicalDescription>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=520]">
+                       <abstract>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </abstract>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=505]">
+                       <tableOfContents>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">agrt</xsl:with-param>
+                               </xsl:call-template>
+                       </tableOfContents>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=521]">
+                       <targetAudience>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </targetAudience>
+               </xsl:for-each>
+               <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
+                       <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"></xsl:variable>
+                       <xsl:choose>
+                               <!-- 01/04 fix -->
+                               <xsl:when test="$controlField008-22='d'">
+                                       <targetAudience authority="marctarget">adolescent</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='e'">
+                                       <targetAudience authority="marctarget">adult</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='g'">
+                                       <targetAudience authority="marctarget">general</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
+                                       <targetAudience authority="marctarget">juvenile</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='a'">
+                                       <targetAudience authority="marctarget">preschool</targetAudience>
+                               </xsl:when>
+                               <xsl:when test="$controlField008-22='f'">
+                                       <targetAudience authority="marctarget">specialized</targetAudience>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:if>
+               <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
+                       <note type="statement of responsibility">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=500]">
+                       <note>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                       </note>
+               </xsl:for-each>
+               
+               <!--3.2 change tmee additional note fields-->
+               
+               <xsl:for-each select="marc:datafield[@tag=506]">
+                       <note type="restrictions">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=510]">
+                       <note  type="citation/reference">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+                       
+               <xsl:for-each select="marc:datafield[@tag=511]">
+                       <note type="performers">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=518]">
+                       <note type="venue">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=530]">
+                       <note  type="additional physical form">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=533]">
+                       <note  type="reproduction">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=534]">
+                       <note  type="original version">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=538]">
+                       <note  type="system details">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+               <xsl:for-each select="marc:datafield[@tag=583]">
+                       <note type="action">
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               
+
+               
+               
+               
+               <xsl:for-each select="marc:datafield[@tag=501 or @tag=502 or @tag=504 or @tag=507 or @tag=508 or  @tag=513 or @tag=514 or @tag=515 or @tag=516 or @tag=522 or @tag=524 or @tag=525 or @tag=526 or @tag=535 or @tag=536 or @tag=540 or @tag=541 or @tag=544 or @tag=545 or @tag=546 or @tag=547 or @tag=550 or @tag=552 or @tag=555 or @tag=556 or @tag=561 or @tag=562 or @tag=565 or @tag=567 or @tag=580 or @tag=581 or @tag=584 or @tag=585 or @tag=586]">
+                       <note>
+                               <xsl:call-template name="uri"></xsl:call-template>
+                               <xsl:variable name="str">
+                                       <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                               <xsl:text> </xsl:text>
+                                       </xsl:for-each>
+                               </xsl:variable>
+                               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
+                       <subject>
+                               <cartographics>
+                                       <coordinates>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">defg</xsl:with-param>
+                                               </xsl:call-template>
+                                       </coordinates>
+                               </cartographics>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=043]">
+                       <subject>
+                               <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
+                                       <geographicCode>
+                                               <xsl:attribute name="authority">
+                                                       <xsl:if test="@code='a'">
+                                                               <xsl:text>marcgac</xsl:text>
+                                                       </xsl:if>
+                                                       <xsl:if test="@code='b'">
+                                                               <xsl:value-of select="following-sibling::marc:subfield[@code=2]"></xsl:value-of>
+                                                       </xsl:if>
+                                                       <xsl:if test="@code='c'">
+                                                               <xsl:text>iso3166</xsl:text>
+                                                       </xsl:if>
+                                               </xsl:attribute>
+                                               <xsl:value-of select="self::marc:subfield"></xsl:value-of>
+                                       </geographicCode>
+                               </xsl:for-each>
+                       </subject>
+               </xsl:for-each>
+               <!-- tmee 2006/11/27 -->
+               <xsl:for-each select="marc:datafield[@tag=255]">
+                       <subject>
+                               <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
+                               <cartographics>
+                                       <xsl:if test="@code='a'">
+                                               <scale>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </scale>
+                                       </xsl:if>
+                                       <xsl:if test="@code='b'">
+                                               <projection>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </projection>
+                                       </xsl:if>
+                                       <xsl:if test="@code='c'">
+                                               <coordinates>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </coordinates>
+                                       </xsl:if>
+                               </cartographics>
+                               </xsl:for-each>
+                       </subject>
+               </xsl:for-each>
+                               
+               <xsl:apply-templates select="marc:datafield[653 >= @tag and @tag >= 600]"></xsl:apply-templates>
+               <xsl:apply-templates select="marc:datafield[@tag=656]"></xsl:apply-templates>
+               <xsl:for-each select="marc:datafield[@tag=752]">
+                       <subject>
+                               <hierarchicalGeographic>
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <country>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </country>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <state>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </state>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='c']">
+                                               <county>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </county>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='d']">
+                                               <city>
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                       </xsl:call-template>
+                                               </city>
+                                       </xsl:for-each>
+                               </hierarchicalGeographic>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
+                       <subject>
+                               <xsl:choose>
+                                       <xsl:when test="@ind1=2">
+                                               <temporal encoding="iso8601" point="start">
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString">
+                                                                       <xsl:value-of select="marc:subfield[@code='b'][1]"></xsl:value-of>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </temporal>
+                                               <temporal encoding="iso8601" point="end">
+                                                       <xsl:call-template name="chopPunctuation">
+                                                               <xsl:with-param name="chopString">
+                                                                       <xsl:value-of select="marc:subfield[@code='b'][2]"></xsl:value-of>
+                                                               </xsl:with-param>
+                                                       </xsl:call-template>
+                                               </temporal>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:for-each select="marc:subfield[@code='b']">
+                                                       <temporal encoding="iso8601">
+                                                               <xsl:call-template name="chopPunctuation">
+                                                                       <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </temporal>
+                                               </xsl:for-each>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </subject>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=050]">
+                       <xsl:for-each select="marc:subfield[@code='b']">
+                               <classification authority="lcc">
+                                       <xsl:if test="../marc:subfield[@code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"></xsl:value-of>
+                                       <xsl:text> </xsl:text>
+                                       <xsl:value-of select="text()"></xsl:value-of>
+                               </classification>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
+                               <classification authority="lcc">
+                                       <xsl:if test="../marc:subfield[@code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:value-of select="../marc:subfield[@code='3']"></xsl:value-of>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="text()"></xsl:value-of>
+                               </classification>
+                       </xsl:for-each>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=082]">
+                       <classification authority="ddc">
+                               <xsl:if test="marc:subfield[@code='2']">
+                                       <xsl:attribute name="edition">
+                                               <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:if>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=080]">
+                       <classification authority="udc">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abx</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=060]">
+                       <classification authority="nlm">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
+                       <classification authority="sudocs">
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
+                       <classification authority="candoc">
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=086]">
+                       <classification>
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                               </xsl:attribute>
+                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=084]">
+                       <classification>
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                               </xsl:attribute>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </classification>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=440]">
+                       <relatedItem type="series">
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">av</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:value-of select="$titleChop" />
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <titleInfo type="nfi">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind2>0">
+                                                       <nonSort>
+                                                               <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                                       </nonSort>
+                                                       <title>
+                                                               <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                                       </title>
+                                                       <xsl:call-template name="part"/>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <title>
+                                                               <xsl:value-of select="$titleChop" />
+                                                       </title>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">av</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=510]">
+                       <relatedItem type="isReferencedBy">
+                               <note>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abcx3</xsl:with-param>
+                                       </xsl:call-template>
+                               </note>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=534]">
+                       <relatedItem type="original">
+                               <xsl:call-template name="relatedTitle"></xsl:call-template>
+                               <xsl:call-template name="relatedName"></xsl:call-template>
+                               <xsl:if test="marc:subfield[@code='b' or @code='c']">
+                                       <originInfo>
+                                               <xsl:for-each select="marc:subfield[@code='c']">
+                                                       <publisher>
+                                                               <xsl:value-of select="."></xsl:value-of>
+                                                       </publisher>
+                                               </xsl:for-each>
+                                               <xsl:for-each select="marc:subfield[@code='b']">
+                                                       <edition>
+                                                               <xsl:value-of select="."></xsl:value-of>
+                                                       </edition>
+                                               </xsl:for-each>
+                                       </originInfo>
+                               </xsl:if>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                               <xsl:for-each select="marc:subfield[@code='z']">
+                                       <identifier type="isbn">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </identifier>
+                               </xsl:for-each>
+                               <xsl:call-template name="relatedNote"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <name type="personal">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aq</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">g</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="termsOfAddress"></xsl:call-template>
+                                       <xsl:call-template name="nameDate"></xsl:call-template>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">dg</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </titleInfo>
+                               <name type="corporate">
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:variable name="tempNamePart">
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">c</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:variable>
+                                       <xsl:if test="normalize-space($tempNamePart)">
+                                               <namePart>
+                                                       <xsl:value-of select="$tempNamePart"></xsl:value-of>
+                                               </namePart>
+                                       </xsl:if>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </titleInfo>
+                               <name type="conference">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">gn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="subfieldSelect">
+                                                                       <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
+                       <relatedItem>
+                               <xsl:call-template name="constituentOrRelatedType"></xsl:call-template>
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:value-of select="$titleChop" />
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <titleInfo type="nfi">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind1>0">
+                                                       <nonSort>
+                                                               <xsl:value-of select="substring($titleChop,1,@ind1)"/>
+                                                       </nonSort>
+                                                       <title>
+                                                               <xsl:value-of select="substring($titleChop,@ind1+1)"/>
+                                                       </title>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <title>
+                                                               <xsl:value-of select="$titleChop" />
+                                                       </title>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
+                       <relatedItem type="series">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
+                       <relatedItem>
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=775]">
+                       <relatedItem type="otherVersion">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
+                       <relatedItem type="constituent">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
+                       <relatedItem type="host">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=776]">
+                       <relatedItem type="otherFormat">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=780]">
+                       <relatedItem type="preceding">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=785]">
+                       <relatedItem type="succeeding">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=786]">
+                       <relatedItem type="original">
+                               <xsl:call-template name="relatedItem76X-78X"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=800]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="part"></xsl:call-template>
+                               </titleInfo>
+                               <name type="personal">
+                                       <namePart>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">aq</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="beforeCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="termsOfAddress"></xsl:call-template>
+                                       <xsl:call-template name="nameDate"></xsl:call-template>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=810]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">dg</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </titleInfo>
+                               <name type="corporate">
+                                       <xsl:for-each select="marc:subfield[@code='a']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <xsl:for-each select="marc:subfield[@code='b']">
+                                               <namePart>
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </namePart>
+                                       </xsl:for-each>
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">c</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="role"></xsl:call-template>
+                               </name>
+                               <xsl:call-template name="relatedForm"></xsl:call-template>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=811]">
+                       <relatedItem type="series">
+                               <titleInfo>
+                                       <title>
+                                               <xsl:call-template name="chopPunctuation">
+                                                       <xsl:with-param name="chopString">
+                                                               <xsl:call-template name="specialSubfieldSelect">
+                                                                       <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
+                                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                                       <xsl:with-param name="afterCodes">g</xsl:with-param>
+                                                               </xsl:call-template>
+                                                       </xsl:with-param>
+                                               </xsl:call-template>
+                                       </title>
+                                       <xsl:call-template name="relatedPartNumName"/>
+                               </titleInfo>
+                               <name type="conference">
+                                       <namePart>
+                                               <xsl:call-template name="specialSubfieldSelect">
+                                                       <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
+                                                       <xsl:with-param name="axis">t</xsl:with-param>
+                                                       <xsl:with-param name="beforeCodes">gn</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                                       <xsl:call-template name="role"/>
+                               </name>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='830']">
+                       <relatedItem type="series">
+                               <xsl:variable name="titleChop">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:variable>
+                               <titleInfo>
+                                       <title>
+                                               <xsl:value-of select="$titleChop" />
+                                       </title>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <titleInfo type="nfi">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind2>0">
+                                                       <nonSort>
+                                                               <xsl:value-of select="substring($titleChop,1,@ind2)"/>
+                                                       </nonSort>
+                                                       <title>
+                                                               <xsl:value-of select="substring($titleChop,@ind2+1)"/>
+                                                       </title>
+                                               </xsl:when>
+                                               <xsl:otherwise>
+                                                       <title>
+                                                               <xsl:value-of select="$titleChop" />
+                                                       </title>
+                                               </xsl:otherwise>
+                                       </xsl:choose>
+                                       <xsl:call-template name="part"/>
+                               </titleInfo>
+                               <xsl:call-template name="relatedForm"/>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
+                       <relatedItem>
+                               <internetMediaType>
+                                       <xsl:value-of select="."/>
+                               </internetMediaType>
+                       </relatedItem>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='020']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">isbn</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="isbn">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">isrc</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="isrc">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">ismn</xsl:with-param>
+                       </xsl:call-template>
+                       <xsl:if test="marc:subfield[@code='a']">
+                               <identifier type="ismn">
+                                       <xsl:value-of select="marc:subfield[@code='a']"/>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">sici</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="sici">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='022']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">issn</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="issn">
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='010']">
+                       <xsl:call-template name="isInvalid">
+                               <xsl:with-param name="type">lccn</xsl:with-param>
+                       </xsl:call-template>
+                       <identifier type="lccn">
+                               <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='028']">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:choose>
+                                               <xsl:when test="@ind1='0'">issue number</xsl:when>
+                                               <xsl:when test="@ind1='1'">matrix number</xsl:when>
+                                               <xsl:when test="@ind1='2'">music plate</xsl:when>
+                                               <xsl:when test="@ind1='3'">music publisher</xsl:when>
+                                               <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
+                                       </xsl:choose>
+                               </xsl:attribute>
+                               <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 028 -->
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">
+                                               <xsl:choose>
+                                                       <xsl:when test="@ind1='0'">ba</xsl:when>
+                                                       <xsl:otherwise>ab</xsl:otherwise>
+                                               </xsl:choose>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='037']">
+                       <identifier type="stock number">
+                               <!--<xsl:call-template name="isInvalid"/>--> <!-- no $z in 037 -->
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">ab</xsl:with-param>
+                               </xsl:call-template>
+                       </identifier>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:choose>
+                                               <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')">doi</xsl:when>
+                                               <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov')">hdl</xsl:when>
+                                               <xsl:otherwise>uri</xsl:otherwise>
+                                       </xsl:choose>
+                               </xsl:attribute>
+                               <xsl:choose>
+                                       <xsl:when test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov') ">
+                                               <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                       </identifier>
+                       <xsl:if test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
+                               <identifier type="hdl">
+                                       <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">y3z</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"></xsl:value-of>
+                               </identifier>
+                       </xsl:if>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
+                       <identifier type="upc">
+                               <xsl:call-template name="isInvalid"/>
+                               <xsl:value-of select="marc:subfield[@code='a']"/>
+                       </identifier>
+               </xsl:for-each>
+               <!-- 1/04 fix added $y -->
+               <xsl:for-each select="marc:datafield[@tag=856][marc:subfield[@code='u']]">
+                       <location>
+                               <url>
+                                       <xsl:if test="marc:subfield[@code='y' or @code='3']">
+                                               <xsl:attribute name="displayLabel">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">y3</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:if test="marc:subfield[@code='z' ]">
+                                               <xsl:attribute name="note">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">z</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="marc:subfield[@code='u']"></xsl:value-of>
+
+                               </url>
+                       </location>
+               </xsl:for-each>
+                       
+                       <!-- 3.2 change tmee 856z  -->
+
+               
+               <xsl:for-each select="marc:datafield[@tag=852]">
+                       <location>
+                               <physicalLocation>
+                                       <xsl:call-template name="displayLabel"></xsl:call-template>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abje</xsl:with-param>
+                                       </xsl:call-template>
+                               </physicalLocation>
+                       </location>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=506]">
+                       <accessCondition type="restrictionOnAccess">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abcd35</xsl:with-param>
+                               </xsl:call-template>
+                       </accessCondition>
+               </xsl:for-each>
+               <xsl:for-each select="marc:datafield[@tag=540]">
+                       <accessCondition type="useAndReproduction">
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">abcde35</xsl:with-param>
+                               </xsl:call-template>
+                       </accessCondition>
+               </xsl:for-each>
+               <recordInfo>
+                       <xsl:for-each select="marc:datafield[@tag=040]">
+                               <recordContentSource authority="marcorg">
+                                       <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                               </recordContentSource>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=008]">
+                               <recordCreationDate encoding="marc">
+                                       <xsl:value-of select="substring(.,1,6)"></xsl:value-of>
+                               </recordCreationDate>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=005]">
+                               <recordChangeDate encoding="iso8601">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </recordChangeDate>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:controlfield[@tag=001]">
+                               <recordIdentifier>
+                                       <xsl:if test="../marc:controlfield[@tag=003]">
+                                               <xsl:attribute name="source">
+                                                       <xsl:value-of select="../marc:controlfield[@tag=003]"></xsl:value-of>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </recordIdentifier>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
+                               <languageOfCataloging>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </languageTerm>
+                               </languageOfCataloging>
+                       </xsl:for-each>
+               </recordInfo>
+       </xsl:template>
+       <xsl:template name="displayForm">
+               <xsl:for-each select="marc:subfield[@code='c']">
+                       <displayForm>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </displayForm>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="affiliation">
+               <xsl:for-each select="marc:subfield[@code='u']">
+                       <affiliation>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </affiliation>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="uri">
+               <xsl:for-each select="marc:subfield[@code='u']">
+                       <xsl:attribute name="xlink:href">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </xsl:attribute>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='0']">
+                       <xsl:choose>
+                               <xsl:when test="contains(text(), ')')">
+                                       <xsl:attribute name="xlink:href">
+                                               <xsl:value-of select="substring-after(text(), ')')"></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:attribute name="xlink:href">
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </xsl:attribute>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="role">
+               <xsl:for-each select="marc:subfield[@code='e']">
+                       <role>
+                               <roleTerm type="text">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </roleTerm>
+                       </role>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='4']">
+                       <role>
+                               <roleTerm authority="marcrelator" type="code">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </roleTerm>
+                       </role>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="part">
+               <xsl:variable name="partNumber">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">n</xsl:with-param>
+                               <xsl:with-param name="anyCodes">n</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:variable name="partName">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">p</xsl:with-param>
+                               <xsl:with-param name="anyCodes">p</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($partNumber))">
+                       <partNumber>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="$partNumber"></xsl:with-param>
+                               </xsl:call-template>
+                       </partNumber>
+               </xsl:if>
+               <xsl:if test="string-length(normalize-space($partName))">
+                       <partName>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="$partName"></xsl:with-param>
+                               </xsl:call-template>
+                       </partName>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedPart">
+               <xsl:if test="@tag=773">
+                       <xsl:for-each select="marc:subfield[@code='g']">
+                               <part>
+                                       <text>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </text>
+                               </part>
+                       </xsl:for-each>
+                       <xsl:for-each select="marc:subfield[@code='q']">
+                               <part>
+                                       <xsl:call-template name="parsePart"></xsl:call-template>
+                               </part>
+                       </xsl:for-each>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedPartNumName">
+               <xsl:variable name="partNumber">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">g</xsl:with-param>
+                               <xsl:with-param name="anyCodes">g</xsl:with-param>
+                               <xsl:with-param name="afterCodes">pst</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:variable name="partName">
+                       <xsl:call-template name="specialSubfieldSelect">
+                               <xsl:with-param name="axis">p</xsl:with-param>
+                               <xsl:with-param name="anyCodes">p</xsl:with-param>
+                               <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="string-length(normalize-space($partNumber))">
+                       <partNumber>
+                               <xsl:value-of select="$partNumber"></xsl:value-of>
+                       </partNumber>
+               </xsl:if>
+               <xsl:if test="string-length(normalize-space($partName))">
+                       <partName>
+                               <xsl:value-of select="$partName"></xsl:value-of>
+                       </partName>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedName">
+               <xsl:for-each select="marc:subfield[@code='a']">
+                       <name>
+                               <namePart>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </namePart>
+                       </name>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedForm">
+               <xsl:for-each select="marc:subfield[@code='h']">
+                       <physicalDescription>
+                               <form>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </form>
+                       </physicalDescription>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedExtent">
+               <xsl:for-each select="marc:subfield[@code='h']">
+                       <physicalDescription>
+                               <extent>
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </extent>
+                       </physicalDescription>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedNote">
+               <xsl:for-each select="marc:subfield[@code='n']">
+                       <note>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </note>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedSubject">
+               <xsl:for-each select="marc:subfield[@code='j']">
+                       <subject>
+                               <temporal encoding="iso8601">
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                       </xsl:call-template>
+                               </temporal>
+                       </subject>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifierISSN">
+               <xsl:for-each select="marc:subfield[@code='x']">
+                       <identifier type="issn">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifierLocal">
+               <xsl:for-each select="marc:subfield[@code='w']">
+                       <identifier type="local">
+                               <xsl:value-of select="."></xsl:value-of>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedIdentifier">
+               <xsl:for-each select="marc:subfield[@code='o']">
+                       <identifier>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </identifier>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedItem76X-78X">
+               <xsl:call-template name="displayLabel"></xsl:call-template>
+               <xsl:call-template name="relatedTitle76X-78X"></xsl:call-template>
+               <xsl:call-template name="relatedName"></xsl:call-template>
+               <xsl:call-template name="relatedOriginInfo"></xsl:call-template>
+               <xsl:call-template name="relatedLanguage"></xsl:call-template>
+               <xsl:call-template name="relatedExtent"></xsl:call-template>
+               <xsl:call-template name="relatedNote"></xsl:call-template>
+               <xsl:call-template name="relatedSubject"></xsl:call-template>
+               <xsl:call-template name="relatedIdentifier"></xsl:call-template>
+               <xsl:call-template name="relatedIdentifierISSN"></xsl:call-template>
+               <xsl:call-template name="relatedIdentifierLocal"></xsl:call-template>
+               <xsl:call-template name="relatedPart"></xsl:call-template>
+       </xsl:template>
+       <xsl:template name="subjectGeographicZ">
+               <geographic>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </geographic>
+       </xsl:template>
+       <xsl:template name="subjectTemporalY">
+               <temporal>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </temporal>
+       </xsl:template>
+       <xsl:template name="subjectTopic">
+               <topic>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </topic>
+       </xsl:template> 
+       <!-- 3.2 change tmee 6xx $v genre -->
+       <xsl:template name="subjectGenre">
+               <genre>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                       </xsl:call-template>
+               </genre>
+       </xsl:template>
+       
+       <xsl:template name="nameABCDN">
+               <xsl:for-each select="marc:subfield[@code='a']">
+                       <namePart>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="."></xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='b']">
+                       <namePart>
+                               <xsl:value-of select="."></xsl:value-of>
+                       </namePart>
+               </xsl:for-each>
+               <xsl:if test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
+                       <namePart>
+                               <xsl:call-template name="subfieldSelect">
+                                       <xsl:with-param name="codes">cdn</xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="nameABCDQ">
+               <namePart>
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString">
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">aq</xsl:with-param>
+                                       </xsl:call-template>
+                               </xsl:with-param>
+                               <xsl:with-param name="punctuation">
+                                       <xsl:text>:,;/ </xsl:text>
+                               </xsl:with-param>
+                       </xsl:call-template>
+               </namePart>
+               <xsl:call-template name="termsOfAddress"></xsl:call-template>
+               <xsl:call-template name="nameDate"></xsl:call-template>
+       </xsl:template>
+       <xsl:template name="nameACDEQ">
+               <namePart>
+                       <xsl:call-template name="subfieldSelect">
+                               <xsl:with-param name="codes">acdeq</xsl:with-param>
+                       </xsl:call-template>
+               </namePart>
+       </xsl:template>
+       <xsl:template name="constituentOrRelatedType">
+               <xsl:if test="@ind2=2">
+                       <xsl:attribute name="type">constituent</xsl:attribute>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedTitle">
+               <xsl:for-each select="marc:subfield[@code='t']">
+                       <titleInfo>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                       </titleInfo>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedTitle76X-78X">
+               <xsl:for-each select="marc:subfield[@code='t']">
+                       <titleInfo>
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='p']">
+                       <titleInfo type="abbreviated">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+               <xsl:for-each select="marc:subfield[@code='s']">
+                       <titleInfo type="uniform">
+                               <title>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </title>
+                               <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
+                                       <xsl:call-template name="relatedPartNumName"></xsl:call-template>
+                               </xsl:if>
+                       </titleInfo>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="relatedOriginInfo">
+               <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
+                       <originInfo>
+                               <xsl:if test="@tag=775">
+                                       <xsl:for-each select="marc:subfield[@code='f']">
+                                               <place>
+                                                       <placeTerm>
+                                                               <xsl:attribute name="type">code</xsl:attribute>
+                                                               <xsl:attribute name="authority">marcgac</xsl:attribute>
+                                                               <xsl:value-of select="."></xsl:value-of>
+                                                       </placeTerm>
+                                               </place>
+                                       </xsl:for-each>
+                               </xsl:if>
+                               <xsl:for-each select="marc:subfield[@code='d']">
+                                       <publisher>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </publisher>
+                               </xsl:for-each>
+                               <xsl:for-each select="marc:subfield[@code='b']">
+                                       <edition>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </edition>
+                               </xsl:for-each>
+                       </originInfo>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="relatedLanguage">
+               <xsl:for-each select="marc:subfield[@code='e']">
+                       <xsl:call-template name="getLanguage">
+                               <xsl:with-param name="langString">
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </xsl:with-param>
+                       </xsl:call-template>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="nameDate">
+               <xsl:for-each select="marc:subfield[@code='d']">
+                       <namePart type="date">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="."></xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="subjectAuthority">
+               <xsl:if test="@ind2!=4">
+                       <xsl:if test="@ind2!=' '">
+                               <xsl:if test="@ind2!=8">
+                                       <xsl:if test="@ind2!=9">
+                                               <xsl:attribute name="authority">
+                                                       <xsl:choose>
+                                                               <xsl:when test="@ind2=0">lcsh</xsl:when>
+                                                               <xsl:when test="@ind2=1">lcshac</xsl:when>
+                                                               <xsl:when test="@ind2=2">mesh</xsl:when>
+                                                               <!-- 1/04 fix -->
+                                                               <xsl:when test="@ind2=3">nal</xsl:when>
+                                                               <xsl:when test="@ind2=5">csh</xsl:when>
+                                                               <xsl:when test="@ind2=6">rvm</xsl:when>
+                                                               <xsl:when test="@ind2=7">
+                                                                       <xsl:value-of select="marc:subfield[@code='2']"></xsl:value-of>
+                                                               </xsl:when>
+                                                       </xsl:choose>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                               </xsl:if>
+                       </xsl:if>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="subjectAnyOrder">
+               <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
+                       <xsl:choose>
+                               <xsl:when test="@code='v'">
+                                       <xsl:call-template name="subjectGenre"></xsl:call-template>
+                               </xsl:when>
+                               <xsl:when test="@code='x'">
+                                       <xsl:call-template name="subjectTopic"></xsl:call-template>
+                               </xsl:when>
+                               <xsl:when test="@code='y'">
+                                       <xsl:call-template name="subjectTemporalY"></xsl:call-template>
+                               </xsl:when>
+                               <xsl:when test="@code='z'">
+                                       <xsl:call-template name="subjectGeographicZ"></xsl:call-template>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:for-each>
+       </xsl:template>
+       <xsl:template name="specialSubfieldSelect">
+               <xsl:param name="anyCodes"></xsl:param>
+               <xsl:param name="axis"></xsl:param>
+               <xsl:param name="beforeCodes"></xsl:param>
+               <xsl:param name="afterCodes"></xsl:param>
+               <xsl:variable name="str">
+                       <xsl:for-each select="marc:subfield">
+                               <xsl:if test="contains($anyCodes, @code)      or (contains($beforeCodes,@code) and following-sibling::marc:subfield[@code=$axis])      or (contains($afterCodes,@code) and preceding-sibling::marc:subfield[@code=$axis])">
+                                       <xsl:value-of select="text()"></xsl:value-of>
+                                       <xsl:text> </xsl:text>
+                               </xsl:if>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:value-of select="substring($str,1,string-length($str)-1)"></xsl:value-of>
+       </xsl:template>
+       
+       <!-- 3.2 change tmee 6xx $v genre -->
+       <xsl:template match="marc:datafield[@tag=600]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <name type="personal">
+                               <xsl:call-template name="uri" />
+                               <namePart>
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString">
+                                                       <xsl:call-template name="subfieldSelect">
+                                                               <xsl:with-param name="codes">aq</xsl:with-param>
+                                                       </xsl:call-template>
+                                               </xsl:with-param>
+                                       </xsl:call-template>
+                               </namePart>
+                               <xsl:call-template name="termsOfAddress"></xsl:call-template>
+                               <xsl:call-template name="nameDate"></xsl:call-template>
+                               <xsl:call-template name="affiliation"></xsl:call-template>
+                               <xsl:call-template name="role"></xsl:call-template>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=610]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <name type="corporate">
+                               <xsl:call-template name="uri" />
+                               <xsl:for-each select="marc:subfield[@code='a']">
+                                       <namePart>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </namePart>
+                               </xsl:for-each>
+                               <xsl:for-each select="marc:subfield[@code='b']">
+                                       <namePart>
+                                               <xsl:value-of select="."></xsl:value-of>
+                                       </namePart>
+                               </xsl:for-each>
+                               <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
+                                       <namePart>
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">cdnp</xsl:with-param>
+                                               </xsl:call-template>
+                                       </namePart>
+                               </xsl:if>
+                               <xsl:call-template name="role"></xsl:call-template>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=611]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <name type="conference">
+                               <xsl:call-template name="uri" />
+                               <namePart>
+                                       <xsl:call-template name="subfieldSelect">
+                                               <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
+                                       </xsl:call-template>
+                               </namePart>
+                               <xsl:for-each select="marc:subfield[@code='4']">
+                                       <role>
+                                               <roleTerm authority="marcrelator" type="code">
+                                                       <xsl:value-of select="."></xsl:value-of>
+                                               </roleTerm>
+                                       </role>
+                               </xsl:for-each>
+                       </name>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=630]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <xsl:variable name="titleChop">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">adfhklor</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:variable>
+                       <titleInfo>
+                               <title>
+                                       <xsl:value-of select="$titleChop" />
+                               </title>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleInfo>
+                       <titleInfo type="nfi">
+                               <xsl:choose>
+                                       <xsl:when test="@ind1>0">
+                                               <nonSort>
+                                                       <xsl:value-of select="substring($titleChop,1,@ind1)"/>
+                                               </nonSort>
+                                               <title>
+                                                       <xsl:value-of select="substring($titleChop,@ind1+1)"/>
+                                               </title>
+                                               <xsl:call-template name="part"/>
+                                       </xsl:when>
+                                       <xsl:otherwise>
+                                               <title>
+                                                       <xsl:value-of select="$titleChop" />
+                                               </title>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+                               <xsl:call-template name="part"></xsl:call-template>
+                       </titleInfo>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=650]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <topic>
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">abcd</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </topic>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=651]">
+               <subject>
+                       <xsl:call-template name="subjectAuthority"></xsl:call-template>
+                       <xsl:for-each select="marc:subfield[@code='a']">
+                               <geographic>
+                                       <xsl:call-template name="uri" />
+                                       <xsl:call-template name="chopPunctuation">
+                                               <xsl:with-param name="chopString" select="."></xsl:with-param>
+                                       </xsl:call-template>
+                               </geographic>
+                       </xsl:for-each>
+                       <xsl:call-template name="subjectAnyOrder"></xsl:call-template>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=653]">
+               <subject>
+                       <xsl:for-each select="marc:subfield[@code='a']">
+                               <topic>
+                                       <xsl:call-template name="uri" />
+                                       <xsl:value-of select="."></xsl:value-of>
+                               </topic>
+                       </xsl:for-each>
+               </subject>
+       </xsl:template>
+       <xsl:template match="marc:datafield[@tag=656]">
+               <subject>
+                       <xsl:if test="marc:subfield[@code=2]">
+                               <xsl:attribute name="authority">
+                                       <xsl:value-of select="marc:subfield[@code=2]"></xsl:value-of>
+                               </xsl:attribute>
+                       </xsl:if>
+                       <occupation>
+                               <xsl:call-template name="uri" />
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="marc:subfield[@code='a']"></xsl:value-of>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </occupation>
+               </subject>
+       </xsl:template>
+       <xsl:template name="termsOfAddress">
+               <xsl:if test="marc:subfield[@code='b' or @code='c']">
+                       <namePart type="termsOfAddress">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">bc</xsl:with-param>
+                                               </xsl:call-template>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </namePart>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="displayLabel">
+               <xsl:if test="marc:subfield[@code='i']">
+                       <xsl:attribute name="displayLabel">
+                               <xsl:value-of select="marc:subfield[@code='i']"></xsl:value-of>
+                       </xsl:attribute>
+               </xsl:if>
+               <xsl:if test="marc:subfield[@code='3']">
+                       <xsl:attribute name="displayLabel">
+                               <xsl:value-of select="marc:subfield[@code='3']"></xsl:value-of>
+                       </xsl:attribute>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="isInvalid">
+               <xsl:param name="type"/>
+               <xsl:if test="marc:subfield[@code='z'] or marc:subfield[@code='y']">
+                       <identifier>
+                               <xsl:attribute name="type">
+                                       <xsl:value-of select="$type"/>
+                               </xsl:attribute>
+                               <xsl:attribute name="invalid">
+                                       <xsl:text>yes</xsl:text>
+                               </xsl:attribute>
+                               <xsl:if test="marc:subfield[@code='z']">
+                                       <xsl:value-of select="marc:subfield[@code='z']"/>
+                               </xsl:if>
+                               <xsl:if test="marc:subfield[@code='y']">
+                                       <xsl:value-of select="marc:subfield[@code='y']"/>
+                               </xsl:if>
+                       </identifier>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="subtitle">
+               <xsl:if test="marc:subfield[@code='b']">
+                       <subTitle>
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString">
+                                               <xsl:value-of select="marc:subfield[@code='b']"/>
+                                               <!--<xsl:call-template name="subfieldSelect">
+                                                       <xsl:with-param name="codes">b</xsl:with-param>                                                                 
+                                               </xsl:call-template>-->
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </subTitle>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="script">
+               <xsl:param name="scriptCode"></xsl:param>
+               <xsl:attribute name="script">
+                       <xsl:choose>
+                               <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
+                               <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
+                               <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
+                               <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
+                               <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
+                               <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
+                       </xsl:choose>
+               </xsl:attribute>
+       </xsl:template>
+       <xsl:template name="parsePart">
+               <!-- assumes 773$q= 1:2:3<4
+                    with up to 3 levels and one optional start page
+               -->
+               <xsl:variable name="level1">
+                       <xsl:choose>
+                               <xsl:when test="contains(text(),':')">
+                                       <!-- 1:2 -->
+                                       <xsl:value-of select="substring-before(text(),':')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:when test="not(contains(text(),':'))">
+                                       <!-- 1 or 1<3 -->
+                                       <xsl:if test="contains(text(),'&lt;')">
+                                               <!-- 1<3 -->
+                                               <xsl:value-of select="substring-before(text(),'&lt;')"></xsl:value-of>
+                                       </xsl:if>
+                                       <xsl:if test="not(contains(text(),'&lt;'))">
+                                               <!-- 1 -->
+                                               <xsl:value-of select="text()"></xsl:value-of>
+                                       </xsl:if>
+                               </xsl:when>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="sici2">
+                       <xsl:choose>
+                               <xsl:when test="starts-with(substring-after(text(),$level1),':')">
+                                       <xsl:value-of select="substring(substring-after(text(),$level1),2)"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="substring-after(text(),$level1)"></xsl:value-of>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="level2">
+                       <xsl:choose>
+                               <xsl:when test="contains($sici2,':')">
+                                       <!--  2:3<4  -->
+                                       <xsl:value-of select="substring-before($sici2,':')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:when test="contains($sici2,'&lt;')">
+                                       <!-- 1: 2<4 -->
+                                       <xsl:value-of select="substring-before($sici2,'&lt;')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="$sici2"></xsl:value-of>
+                                       <!-- 1:2 -->
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="sici3">
+                       <xsl:choose>
+                               <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
+                                       <xsl:value-of select="substring(substring-after($sici2,$level2),2)"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="substring-after($sici2,$level2)"></xsl:value-of>
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="level3">
+                       <xsl:choose>
+                               <xsl:when test="contains($sici3,'&lt;')">
+                                       <!-- 2<4 -->
+                                       <xsl:value-of select="substring-before($sici3,'&lt;')"></xsl:value-of>
+                               </xsl:when>
+                               <xsl:otherwise>
+                                       <xsl:value-of select="$sici3"></xsl:value-of>
+                                       <!-- 3 -->
+                               </xsl:otherwise>
+                       </xsl:choose>
+               </xsl:variable>
+               <xsl:variable name="page">
+                       <xsl:if test="contains(text(),'&lt;')">
+                               <xsl:value-of select="substring-after(text(),'&lt;')"></xsl:value-of>
+                       </xsl:if>
+               </xsl:variable>
+               <xsl:if test="$level1">
+                       <detail level="1">
+                               <number>
+                                       <xsl:value-of select="$level1"></xsl:value-of>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$level2">
+                       <detail level="2">
+                               <number>
+                                       <xsl:value-of select="$level2"></xsl:value-of>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$level3">
+                       <detail level="3">
+                               <number>
+                                       <xsl:value-of select="$level3"></xsl:value-of>
+                               </number>
+                       </detail>
+               </xsl:if>
+               <xsl:if test="$page">
+                       <extent unit="page">
+                               <start>
+                                       <xsl:value-of select="$page"></xsl:value-of>
+                               </start>
+                       </extent>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="getLanguage">
+               <xsl:param name="langString"></xsl:param>
+               <xsl:param name="controlField008-35-37"></xsl:param>
+               <xsl:variable name="length" select="string-length($langString)"></xsl:variable>
+               <xsl:choose>
+                       <xsl:when test="$length=0"></xsl:when>
+                       <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
+                               <xsl:call-template name="getLanguage">
+                                       <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
+                                       <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <language>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="substring($langString,1,3)"></xsl:value-of>
+                                       </languageTerm>
+                               </language>
+                               <xsl:call-template name="getLanguage">
+                                       <xsl:with-param name="langString" select="substring($langString,4,$length)"></xsl:with-param>
+                                       <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"></xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="isoLanguage">
+               <xsl:param name="currentLanguage"></xsl:param>
+               <xsl:param name="usedLanguages"></xsl:param>
+               <xsl:param name="remainingLanguages"></xsl:param>
+               <xsl:choose>
+                       <xsl:when test="string-length($currentLanguage)=0"></xsl:when>
+                       <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
+                               <language>
+                                       <xsl:if test="@code!='a'">
+                                               <xsl:attribute name="objectPart">
+                                                       <xsl:choose>
+                                                               <xsl:when test="@code='b'">summary or subtitle</xsl:when>
+                                                               <xsl:when test="@code='d'">sung or spoken text</xsl:when>
+                                                               <xsl:when test="@code='e'">libretto</xsl:when>
+                                                               <xsl:when test="@code='f'">table of contents</xsl:when>
+                                                               <xsl:when test="@code='g'">accompanying material</xsl:when>
+                                                               <xsl:when test="@code='h'">translation</xsl:when>
+                                                       </xsl:choose>
+                                               </xsl:attribute>
+                                       </xsl:if>
+                                       <languageTerm authority="iso639-2b" type="code">
+                                               <xsl:value-of select="$currentLanguage"></xsl:value-of>
+                                       </languageTerm>
+                               </language>
+                               <xsl:call-template name="isoLanguage">
+                                       <xsl:with-param name="currentLanguage">
+                                               <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="usedLanguages">
+                                               <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="remainingLanguages">
+                                               <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:otherwise>
+                               <xsl:call-template name="isoLanguage">
+                                       <xsl:with-param name="currentLanguage">
+                                               <xsl:value-of select="substring($remainingLanguages,1,3)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="usedLanguages">
+                                               <xsl:value-of select="concat($usedLanguages,$currentLanguage)"></xsl:value-of>
+                                       </xsl:with-param>
+                                       <xsl:with-param name="remainingLanguages">
+                                               <xsl:value-of select="substring($remainingLanguages,4,string-length($remainingLanguages))"></xsl:value-of>
+                                       </xsl:with-param>
+                               </xsl:call-template>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="chopBrackets">
+               <xsl:param name="chopString"></xsl:param>
+               <xsl:variable name="string">
+                       <xsl:call-template name="chopPunctuation">
+                               <xsl:with-param name="chopString" select="$chopString"></xsl:with-param>
+                       </xsl:call-template>
+               </xsl:variable>
+               <xsl:if test="substring($string, 1,1)='['">
+                       <xsl:value-of select="substring($string,2, string-length($string)-2)"></xsl:value-of>
+               </xsl:if>
+               <xsl:if test="substring($string, 1,1)!='['">
+                       <xsl:value-of select="$string"></xsl:value-of>
+               </xsl:if>
+       </xsl:template>
+       <xsl:template name="rfcLanguages">
+               <xsl:param name="nodeNum"></xsl:param>
+               <xsl:param name="usedLanguages"></xsl:param>
+               <xsl:param name="controlField008-35-37"></xsl:param>
+               <xsl:variable name="currentLanguage" select="."></xsl:variable>
+               <xsl:choose>
+                       <xsl:when test="not($currentLanguage)"></xsl:when>
+                       <xsl:when test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
+                               <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
+                                       <language>
+                                               <xsl:if test="@code!='a'">
+                                                       <xsl:attribute name="objectPart">
+                                                               <xsl:choose>
+                                                                       <xsl:when test="@code='b'">summary or subtitle</xsl:when>
+                                                                       <xsl:when test="@code='d'">sung or spoken text</xsl:when>
+                                                                       <xsl:when test="@code='e'">libretto</xsl:when>
+                                                                       <xsl:when test="@code='f'">table of contents</xsl:when>
+                                                                       <xsl:when test="@code='g'">accompanying material</xsl:when>
+                                                                       <xsl:when test="@code='h'">translation</xsl:when>
+                                                               </xsl:choose>
+                                                       </xsl:attribute>
+                                               </xsl:if>
+                                               <languageTerm authority="rfc3066" type="code">
+                                                       <xsl:value-of select="$currentLanguage"/>
+                                               </languageTerm>
+                                       </language>
+                               </xsl:if>
+                       </xsl:when>
+                       <xsl:otherwise>
+                       </xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+       <xsl:template name="datafield">
+               <xsl:param name="tag"/>
+               <xsl:param name="ind1"><xsl:text> </xsl:text></xsl:param>
+               <xsl:param name="ind2"><xsl:text> </xsl:text></xsl:param>
+               <xsl:param name="subfields"/>
+               <xsl:element name="marc:datafield">
+                       <xsl:attribute name="tag">
+                               <xsl:value-of select="$tag"/>
+                       </xsl:attribute>
+                       <xsl:attribute name="ind1">
+                               <xsl:value-of select="$ind1"/>
+                       </xsl:attribute>
+                       <xsl:attribute name="ind2">
+                               <xsl:value-of select="$ind2"/>
+                       </xsl:attribute>
+                       <xsl:copy-of select="$subfields"/>
+               </xsl:element>
+       </xsl:template>
+
+       <xsl:template name="subfieldSelect">
+               <xsl:param name="codes"/>
+               <xsl:param name="delimeter"><xsl:text> </xsl:text></xsl:param>
+               <xsl:variable name="str">
+                       <xsl:for-each select="marc:subfield">
+                               <xsl:if test="contains($codes, @code)">
+                                       <xsl:value-of select="text()"/><xsl:value-of select="$delimeter"/>
+                               </xsl:if>
+                       </xsl:for-each>
+               </xsl:variable>
+               <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
+       </xsl:template>
+
+       <xsl:template name="buildSpaces">
+               <xsl:param name="spaces"/>
+               <xsl:param name="char"><xsl:text> </xsl:text></xsl:param>
+               <xsl:if test="$spaces>0">
+                       <xsl:value-of select="$char"/>
+                       <xsl:call-template name="buildSpaces">
+                               <xsl:with-param name="spaces" select="$spaces - 1"/>
+                               <xsl:with-param name="char" select="$char"/>
+                       </xsl:call-template>
+               </xsl:if>
+       </xsl:template>
+
+       <xsl:template name="chopPunctuation">
+               <xsl:param name="chopString"/>
+               <xsl:param name="punctuation"><xsl:text>.:,;/ </xsl:text></xsl:param>
+               <xsl:variable name="length" select="string-length($chopString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
+                               <xsl:call-template name="chopPunctuation">
+                                       <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
+                                       <xsl:with-param name="punctuation" select="$punctuation"/>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:when test="not($chopString)"/>
+                       <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+
+       <xsl:template name="chopPunctuationFront">
+               <xsl:param name="chopString"/>
+               <xsl:variable name="length" select="string-length($chopString)"/>
+               <xsl:choose>
+                       <xsl:when test="$length=0"/>
+                       <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
+                               <xsl:call-template name="chopPunctuationFront">
+                                       <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"/>
+                               </xsl:call-template>
+                       </xsl:when>
+                       <xsl:when test="not($chopString)"/>
+                       <xsl:otherwise><xsl:value-of select="$chopString"/></xsl:otherwise>
+               </xsl:choose>
+       </xsl:template>
+</xsl:stylesheet>$$ WHERE name = 'mods32';
+
+CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
+DECLARE
+    bib     biblio.record_entry%ROWTYPE;
+    idx     config.metabib_field%ROWTYPE;
+    xfrm        config.xml_transform%ROWTYPE;
+    prev_xfrm   TEXT;
+    transformed_xml TEXT;
+    xml_node    TEXT;
+    xml_node_list   TEXT[];
+    facet_text  TEXT;
+    browse_text TEXT;
+    sort_value  TEXT;
+    raw_text    TEXT;
+    curr_text   TEXT;
+    joiner      TEXT := default_joiner; -- XXX will index defs supply a joiner?
+    authority_text TEXT;
+    authority_link BIGINT;
+    output_row  metabib.field_entry_template%ROWTYPE;
+BEGIN
+
+    -- Start out with no field-use bools set
+    output_row.browse_field = FALSE;
+    output_row.facet_field = FALSE;
+    output_row.search_field = FALSE;
+
+    -- Get the record
+    SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
+
+    -- Loop over the indexing entries
+    FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
+
+        joiner := COALESCE(idx.joiner, default_joiner);
+
+        SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
+
+        -- See if we can skip the XSLT ... it's expensive
+        IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+            -- Can't skip the transform
+            IF xfrm.xslt <> '---' THEN
+                transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
+            ELSE
+                transformed_xml := bib.marc;
+            END IF;
+
+            prev_xfrm := xfrm.name;
+        END IF;
+
+        xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+
+        raw_text := NULL;
+        FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
+            CONTINUE WHEN xml_node !~ E'^\\s*<';
+
+            -- XXX much of this should be moved into oils_xpath_string...
+            curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
+                oils_xpath( '//text()',
+                    REGEXP_REPLACE(
+                        REGEXP_REPLACE( -- This escapes all &s not followed by "amp;".  Data ise returned from oils_xpath (above) in UTF-8, not entity encoded
+                            REGEXP_REPLACE( -- This escapes embeded <s
+                                xml_node,
+                                $re$(>[^<]+)(<)([^>]+<)$re$,
+                                E'\\1&lt;\\3',
+                                'g'
+                            ),
+                            '&(?!amp;)',
+                            '&amp;',
+                            'g'
+                        ),
+                        E'\\s+',
+                        ' ',
+                        'g'
+                    )
+                ), ' '), ''),
+                joiner
+            );
+
+            CONTINUE WHEN curr_text IS NULL OR curr_text = '';
+
+            IF raw_text IS NOT NULL THEN
+                raw_text := raw_text || joiner;
+            END IF;
+
+            raw_text := COALESCE(raw_text,'') || curr_text;
+
+            -- autosuggest/metabib.browse_entry
+            IF idx.browse_field THEN
+
+                IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
+                    browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    browse_text := curr_text;
+                END IF;
+
+                IF idx.browse_sort_xpath IS NOT NULL AND
+                    idx.browse_sort_xpath <> '' THEN
+
+                    sort_value := oils_xpath_string(
+                        idx.browse_sort_xpath, xml_node, joiner,
+                        ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
+                    );
+                ELSE
+                    sort_value := browse_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
+                output_row.sort_value :=
+                    public.naco_normalize(sort_value);
+
+                output_row.authority := NULL;
+
+                IF idx.authority_xpath IS NOT NULL AND idx.authority_xpath <> '' THEN
+                    authority_text := oils_xpath_string(
+                        idx.authority_xpath, xml_node, joiner,
+                        ARRAY[
+                            ARRAY[xfrm.prefix, xfrm.namespace_uri],
+                            ARRAY['xlink','http://www.w3.org/1999/xlink']
+                        ]
+                    );
+
+                    IF authority_text ~ '^\d+$' THEN
+                        authority_link := authority_text::BIGINT;
+                        PERFORM * FROM authority.record_entry WHERE id = authority_link;
+                        IF FOUND THEN
+                            output_row.authority := authority_link;
+                        END IF;
+                    END IF;
+
+                END IF;
+
+                output_row.browse_field = TRUE;
+                -- Returning browse rows with search_field = true for search+browse
+                -- configs allows us to retain granularity of being able to search
+                -- browse fields with "starts with" type operators (for example, for
+                -- titles of songs in music albums)
+                IF idx.search_field THEN
+                    output_row.search_field = TRUE;
+                END IF;
+                RETURN NEXT output_row;
+                output_row.browse_field = FALSE;
+                output_row.search_field = FALSE;
+                output_row.sort_value := NULL;
+            END IF;
+
+            -- insert raw node text for faceting
+            IF idx.facet_field THEN
+
+                IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
+                    facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    facet_text := curr_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = -1 * idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
+
+                output_row.facet_field = TRUE;
+                RETURN NEXT output_row;
+                output_row.facet_field = FALSE;
+            END IF;
+
+        END LOOP;
+
+        CONTINUE WHEN raw_text IS NULL OR raw_text = '';
+
+        -- insert combined node text for searching
+        IF idx.search_field THEN
+            output_row.field_class = idx.field_class;
+            output_row.field = idx.id;
+            output_row.source = rid;
+            output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
+
+            output_row.search_field = TRUE;
+            RETURN NEXT output_row;
+            output_row.search_field = FALSE;
+        END IF;
+
+    END LOOP;
+
+END;
+
+$func$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
+DECLARE
+    fclass          RECORD;
+    ind_data        metabib.field_entry_template%ROWTYPE;
+    mbe_row         metabib.browse_entry%ROWTYPE;
+    mbe_id          BIGINT;
+    b_skip_facet    BOOL;
+    b_skip_browse   BOOL;
+    b_skip_search   BOOL;
+    value_prepped   TEXT;
+BEGIN
+
+    SELECT COALESCE(NULLIF(skip_facet, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_facet_indexing' AND enabled)) INTO b_skip_facet;
+    SELECT COALESCE(NULLIF(skip_browse, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_browse_indexing' AND enabled)) INTO b_skip_browse;
+    SELECT COALESCE(NULLIF(skip_search, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_search_indexing' AND enabled)) INTO b_skip_search;
+
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        IF NOT b_skip_search 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;
+        END IF;
+        IF NOT b_skip_facet THEN
+            DELETE FROM metabib.facet_entry WHERE source = bib_id;
+        END IF;
+        IF NOT b_skip_browse THEN
+            DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
+        END IF;
+    END IF;
+
+    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;
+        END IF;
+
+        IF ind_data.facet_field AND NOT b_skip_facet THEN
+            INSERT INTO metabib.facet_entry (field, source, value)
+                VALUES (ind_data.field, ind_data.source, ind_data.value);
+        END IF;
+
+        IF ind_data.browse_field AND NOT b_skip_browse THEN
+            -- A caveat about this SELECT: this should take care of replacing
+            -- old mbe rows when data changes, but not if normalization (by
+            -- which I mean specifically the output of
+            -- evergreen.oils_tsearch2()) changes.  It may or may not be
+            -- expensive to add a comparison of index_vector to index_vector
+            -- to the WHERE clause below.
+
+            value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
+            SELECT INTO mbe_row * FROM metabib.browse_entry
+                WHERE value = value_prepped AND sort_value = ind_data.sort_value;
+
+            IF FOUND THEN
+                mbe_id := mbe_row.id;
+            ELSE
+                INSERT INTO metabib.browse_entry
+                    ( value, sort_value ) VALUES
+                    ( value_prepped, ind_data.sort_value );
+
+                mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
+            END IF;
+
+            INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
+                VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
+        END IF;
+
+        IF ind_data.search_field AND NOT b_skip_search THEN
+            -- Avoid inserting duplicate rows
+            EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
+                '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
+                INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
+                -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
+            IF mbe_id IS NULL THEN
+                EXECUTE $$
+                INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
+                    VALUES ($$ ||
+                        quote_literal(ind_data.field) || $$, $$ ||
+                        quote_literal(ind_data.source) || $$, $$ ||
+                        quote_literal(ind_data.value) ||
+                    $$);$$;
+            END IF;
+        END IF;
+
+    END LOOP;
+
+    IF NOT b_skip_search THEN
+        PERFORM metabib.update_combined_index_vectors(bib_id);
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+-- Don't use Title Proper search field as the browse field
+UPDATE config.metabib_field SET browse_field = FALSE, browse_xpath = NULL, browse_sort_xpath = NULL WHERE id = 6;
+
+-- Create a new Title Proper browse config
+INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, authority_xpath, browse_field, browse_sort_xpath ) VALUES
+    (31, 'title', 'browse', oils_i18n_gettext(31, 'Title Proper (Browse)', 'cmf', 'label'), 'mods32', $$//mods32:mods/mods32:titleBrowse$$, FALSE, '//@xlink:href', TRUE, $$*[local-name() != "nonSort"]$$ );
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0845', :eg_version);
+
+ALTER FUNCTION metabib.browse_pivot (integer[], text) STABLE;
+ALTER FUNCTION metabib.browse_bib_pivot (integer[], text) STABLE;
+ALTER FUNCTION metabib.browse_authority_pivot (integer[], text) STABLE;
+ALTER FUNCTION metabib.browse_authority_refs_pivot (integer[], text) STABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0846', :eg_version);
+
+CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT, force_add INT ) RETURNS TEXT AS $_$
+
+    use MARC::Record;
+    use MARC::File::XML (BinaryEncoding => 'UTF-8');
+    use MARC::Charset;
+    use strict;
+
+    MARC::Charset->assume_unicode(1);
+
+    my $target_xml = shift;
+    my $source_xml = shift;
+    my $field_spec = shift;
+    my $force_add = shift || 0;
+
+    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) {
+                    next if (exists($fields{$f}{match}) and !$force_add);
+                    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}));
+                        }
+                        for my $old_sf ($from_field->subfields) {
+                            $to_field->add_subfields( @$old_sf ) if grep(/$$old_sf[0]/,@{$fields{$f}{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;
+
+SELECT evergreen.upgrade_deps_block_check('0847', :eg_version);
+
+CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
+DECLARE
+    cset                INT;
+    main_entry          authority.control_set_authority_field%ROWTYPE;
+    bib_field           authority.control_set_bib_field%ROWTYPE;
+    auth_id             INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
+    tmp_data            XML;
+    replace_data        XML[] DEFAULT '{}'::XML[];
+    replace_rules       TEXT[] DEFAULT '{}'::TEXT[];
+    auth_field          XML[];
+    auth_i1             TEXT;
+    auth_i2             TEXT;
+BEGIN
+    IF auth_id IS NULL THEN
+        RETURN NULL;
+    END IF;
+
+    -- Default to the LoC controll set
+    SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    -- if none, make a best guess
+    IF cset IS NULL THEN
+        SELECT  control_set INTO cset
+          FROM  authority.control_set_authority_field
+          WHERE tag IN (
+                    SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marc::XML)::TEXT[])
+                      FROM  authority.record_entry
+                      WHERE id = auth_id
+                )
+          LIMIT 1;
+    END IF;
+
+    -- if STILL none, no-op change
+    IF cset IS NULL THEN
+        RETURN XMLELEMENT(
+            name record,
+            XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
+            XMLELEMENT( name leader, '00881nam a2200193   4500'),
+            XMLELEMENT(
+                name datafield,
+                XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
+                XMLELEMENT(
+                                      name subfield,
+                    XMLATTRIBUTES('d' AS code),
+                    '901c'
+                )
+            )
+        )::TEXT;
+    END IF;
+
+    FOR main_entry IN SELECT * FROM authority.control_set_authority_field acsaf WHERE acsaf.control_set = cset AND acsaf.main_entry IS NULL LOOP
+        auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
+        auth_i1 = (XPATH('@ind1',auth_field[1]))[1];
+        auth_i2 = (XPATH('@ind2',auth_field[1]))[1];
+        IF ARRAY_LENGTH(auth_field,1) > 0 THEN
+            FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
+                SELECT XMLELEMENT( -- XMLAGG avoids magical <element> creation, but requires unnest subquery
+                    name datafield,
+                    XMLATTRIBUTES(bib_field.tag AS tag, auth_i1 AS ind1, auth_i2 AS ind2),
+                    XMLAGG(UNNEST)
+                ) INTO tmp_data FROM UNNEST(XPATH('//*[local-name()="subfield"]', auth_field[1]));
+                replace_data := replace_data || tmp_data;
+                replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
+                tmp_data = NULL;
+            END LOOP;
+            EXIT;
+        END IF;
+    END LOOP;
+
+    SELECT XMLAGG(UNNEST) INTO tmp_data FROM UNNEST(replace_data);
+
+    RETURN XMLELEMENT(
+        name record,
+        XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
+        XMLELEMENT( name leader, '00881nam a2200193   4500'),
+        tmp_data,
+        XMLELEMENT(
+            name datafield,
+                         XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
+            XMLELEMENT(
+                name subfield,
+                XMLATTRIBUTES('r' AS code),
+                ARRAY_TO_STRING(replace_rules,',')
+            )
+        )
+    )::TEXT;
+END;
+$f$ STABLE LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0848', :eg_version);
+
+CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
+DECLARE
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    sf_node         TEXT;
+    tag_node        TEXT;
+    thes_code       TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    tmp_text        TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT;
+BEGIN
+    SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    IF cset IS NULL THEN
+        SELECT  control_set INTO cset
+          FROM  authority.control_set_authority_field
+          WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
+          LIMIT 1;
+    END IF;
+
+    thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
+    IF thes_code IS NULL THEN
+        thes_code := '|';
+    ELSIF thes_code = 'z' THEN
+        thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
+    END IF;
+
+    heading_text := '';
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+        first_sf := TRUE;
+
+        FOR tag_node IN SELECT unnest(oils_xpath('//*[@tag="'||tag_used||'"]',marcxml)) LOOP
+            FOR sf_node IN SELECT unnest(oils_xpath('//*[contains("'||acsaf.sf_list||'",@code)]',tag_node)) LOOP
+
+                tmp_text := oils_xpath_string('.', sf_node);
+                sf := oils_xpath_string('./@code', sf_node);
+
+                IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
+
+                    tmp_text := SUBSTRING(
+                        tmp_text FROM
+                        COALESCE(
+                            NULLIF(
+                                REGEXP_REPLACE(
+                                    oils_xpath_string('./@ind'||nfi_used, tag_node),
+                                    $$\D+$$,
+                                    '',
+                                    'g'
+                                ),
+                                ''
+                            )::INT,
+                            0
+                        ) + 1
+                    );
+
+                END IF;
+
+                first_sf := FALSE;
+
+                IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
+                    heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
+                END IF;
+            END LOOP;
+
+            EXIT WHEN heading_text <> '';
+        END LOOP;
+
+        EXIT WHEN heading_text <> '';
+    END LOOP;
+
+    IF heading_text <> '' THEN
+        IF no_thesaurus IS TRUE THEN
+            heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
+        ELSE
+            heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
+        END IF;
+    ELSE
+        heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
+    END IF;
+
+    RETURN heading_text;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
+DECLARE
+    res             authority.simple_heading%ROWTYPE;
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    sort_text       TEXT;
+    tmp_text        TEXT;
+    tmp_xml         TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
+BEGIN
+
+    res.record := auth_id;
+
+    SELECT  control_set INTO cset
+      FROM  authority.control_set_authority_field
+      WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]) )
+      LIMIT 1;
+
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
+
+        res.atag := acsaf.id;
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+
+        FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
+
+            heading_text := public.naco_normalize(
+                COALESCE(
+                    oils_xpath_string('//*[contains("'||acsaf.sf_list||'",@code)]',tmp_xml::TEXT, ' '),
+                    ''
+                )
+            );
+
+            IF nfi_used IS NOT NULL THEN
+
+                sort_text := SUBSTRING(
+                    heading_text FROM
+                    COALESCE(
+                        NULLIF(
+                            REGEXP_REPLACE(
+                                oils_xpath_string('./@ind'||nfi_used, tmp_xml::TEXT),
+                                $$\D+$$,
+                                '',
+                                'g'
+                            ),
+                            ''
+                        )::INT,
+                        0
+                    ) + 1
+                );
+
+            ELSE
+                sort_text := heading_text;
+            END IF;
+
+            IF heading_text IS NOT NULL AND heading_text <> '' THEN
+                res.value := heading_text;
+                res.sort_value := sort_text;
+                RETURN NEXT res;
+            END IF;
+
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0849', :eg_version);
+
+UPDATE config.global_flag
+    SET label = 'Circ: Use original circulation library on desk renewal instead of the workstation library'
+    WHERE name = 'circ.desk_renewal.use_original_circ_lib';
+
+SELECT evergreen.upgrade_deps_block_check('0850', :eg_version);
+
+CREATE OR REPLACE FUNCTION unapi.mra ( obj_id BIGINT, format TEXT,  ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
+        SELECT  XMLELEMENT(
+                    name attributes,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2@mra/' || mra.id AS id,
+                        'tag:open-ils.org:U2@bre/' || mra.id AS record
+                    ),
+                    (SELECT XMLAGG(foo.y)
+                      FROM (SELECT XMLELEMENT(
+                                name field,
+                                XMLATTRIBUTES(
+                                    key AS name,
+                                    cvm.value AS "coded-value",
+                                    cvm.id AS "cvmid",
+                                    rad.filter,
+                                    rad.sorter
+                                ),
+                                x.value
+                            )
+                           FROM EACH(mra.attrs) AS x
+                                JOIN config.record_attr_definition rad ON (x.key = rad.name)
+                                LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = x.key AND code = x.value)
+                        )foo(y)
+                    )
+                )
+          FROM  metabib.record_attr mra
+          WHERE mra.id = $1;
+$F$ LANGUAGE SQL STABLE;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0851', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.maintain_901 () RETURNS TRIGGER AS $func$
+use strict;
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+use MARC::Charset;
+use Encode;
+use Unicode::Normalize;
+
+MARC::Charset->assume_unicode(1);
+
+my $schema = $_TD->{table_schema};
+my $marc = MARC::Record->new_from_xml($_TD->{new}{marc});
+
+my @old901s = $marc->field('901');
+$marc->delete_fields(@old901s);
+
+if ($schema eq 'biblio') {
+    my $tcn_value = $_TD->{new}{tcn_value};
+
+    # Set TCN value to record ID?
+    my $id_as_tcn = spi_exec_query("
+        SELECT enabled
+        FROM config.global_flag
+        WHERE name = 'cat.bib.use_id_for_tcn'
+    ");
+    if (($id_as_tcn->{processed}) && $id_as_tcn->{rows}[0]->{enabled} eq 't') {
+        $tcn_value = $_TD->{new}{id}; 
+        $_TD->{new}{tcn_value} = $tcn_value;
+    }
+
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "a" => $tcn_value,
+        "b" => $_TD->{new}{tcn_source},
+        "c" => $_TD->{new}{id},
+        "t" => $schema
+    );
+
+    if ($_TD->{new}{owner}) {
+        $new_901->add_subfields("o" => $_TD->{new}{owner});
+    }
+
+    if ($_TD->{new}{share_depth}) {
+        $new_901->add_subfields("d" => $_TD->{new}{share_depth});
+    }
+
+    $marc->append_fields($new_901);
+} elsif ($schema eq 'authority') {
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "c" => $_TD->{new}{id},
+        "t" => $schema,
+    );
+    $marc->append_fields($new_901);
+} elsif ($schema eq 'serial') {
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "c" => $_TD->{new}{id},
+        "t" => $schema,
+        "o" => $_TD->{new}{owning_lib},
+    );
+
+    if ($_TD->{new}{record}) {
+        $new_901->add_subfields("r" => $_TD->{new}{record});
+    }
+
+    $marc->append_fields($new_901);
+} else {
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "c" => $_TD->{new}{id},
+        "t" => $schema,
+    );
+    $marc->append_fields($new_901);
+}
+
+my $xml = $marc->as_xml_record();
+$xml =~ s/\n//sgo;
+$xml =~ s/^<\?xml.+\?\s*>//go;
+$xml =~ s/>\s+</></go;
+$xml =~ s/\p{Cc}//go;
+
+# Embed a version of OpenILS::Application::AppUtils->entityize()
+# to avoid having to set PERL5LIB for PostgreSQL as well
+
+$xml = NFC($xml);
+
+# Convert raw ampersands to entities
+$xml =~ s/&(?!\S+;)/&amp;/gso;
+
+# Convert Unicode characters to entities
+$xml =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
+
+$xml =~ s/[\x00-\x1f]//go;
+$_TD->{new}{marc} = $xml;
+
+return "MODIFY";
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION maintain_control_numbers() RETURNS TRIGGER AS $func$
+use strict;
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+use MARC::Charset;
+use Encode;
+use Unicode::Normalize;
+
+MARC::Charset->assume_unicode(1);
+
+my $record = MARC::Record->new_from_xml($_TD->{new}{marc});
+my $schema = $_TD->{table_schema};
+my $rec_id = $_TD->{new}{id};
+
+# Short-circuit if maintaining control numbers per MARC21 spec is not enabled
+my $enable = spi_exec_query("SELECT enabled FROM config.global_flag WHERE name = 'cat.maintain_control_numbers'");
+if (!($enable->{processed}) or $enable->{rows}[0]->{enabled} eq 'f') {
+    return;
+}
+
+# Get the control number identifier from an OU setting based on $_TD->{new}{owner}
+my $ou_cni = 'EVRGRN';
+
+my $owner;
+if ($schema eq 'serial') {
+    $owner = $_TD->{new}{owning_lib};
+} else {
+    # are.owner and bre.owner can be null, so fall back to the consortial setting
+    $owner = $_TD->{new}{owner} || 1;
+}
+
+my $ous_rv = spi_exec_query("SELECT value FROM actor.org_unit_ancestor_setting('cat.marc_control_number_identifier', $owner)");
+if ($ous_rv->{processed}) {
+    $ou_cni = $ous_rv->{rows}[0]->{value};
+    $ou_cni =~ s/"//g; # Stupid VIM syntax highlighting"
+} else {
+    # Fall back to the shortname of the OU if there was no OU setting
+    $ous_rv = spi_exec_query("SELECT shortname FROM actor.org_unit WHERE id = $owner");
+    if ($ous_rv->{processed}) {
+        $ou_cni = $ous_rv->{rows}[0]->{shortname};
+    }
+}
+
+my ($create, $munge) = (0, 0);
+
+my @scns = $record->field('035');
+
+foreach my $id_field ('001', '003') {
+    my $spec_value;
+    my @controls = $record->field($id_field);
+
+    if ($id_field eq '001') {
+        $spec_value = $rec_id;
+    } else {
+        $spec_value = $ou_cni;
+    }
+
+    # Create the 001/003 if none exist
+    if (scalar(@controls) == 1) {
+        # Only one field; check to see if we need to munge it
+        unless (grep $_->data() eq $spec_value, @controls) {
+            $munge = 1;
+        }
+    } else {
+        # Delete the other fields, as with more than 1 001/003 we do not know which 003/001 to match
+        foreach my $control (@controls) {
+            $record->delete_field($control);
+        }
+        $record->insert_fields_ordered(MARC::Field->new($id_field, $spec_value));
+        $create = 1;
+    }
+}
+
+my $cn = $record->field('001')->data();
+# Special handling of OCLC numbers, often found in records that lack 003
+if ($cn =~ /^o(c[nm]|n)\d/) {
+    $cn =~ s/^o(c[nm]|n)0*(\d+)/$2/;
+    $record->field('003')->data('OCoLC');
+    $create = 0;
+}
+
+# Now, if we need to munge the 001, we will first push the existing 001/003
+# into the 035; but if the record did not have one (and one only) 001 and 003
+# to begin with, skip this process
+if ($munge and not $create) {
+
+    my $scn = "(" . $record->field('003')->data() . ")" . $cn;
+
+    # Do not create duplicate 035 fields
+    unless (grep $_->subfield('a') eq $scn, @scns) {
+        $record->insert_fields_ordered(MARC::Field->new('035', '', '', 'a' => $scn));
+    }
+}
+
+# Set the 001/003 and update the MARC
+if ($create or $munge) {
+    $record->field('001')->data($rec_id);
+    $record->field('003')->data($ou_cni);
+
+    my $xml = $record->as_xml_record();
+    $xml =~ s/\n//sgo;
+    $xml =~ s/^<\?xml.+\?\s*>//go;
+    $xml =~ s/>\s+</></go;
+    $xml =~ s/\p{Cc}//go;
+
+    # Embed a version of OpenILS::Application::AppUtils->entityize()
+    # to avoid having to set PERL5LIB for PostgreSQL as well
+
+    $xml = NFC($xml);
+
+    # Convert raw ampersands to entities
+    $xml =~ s/&(?!\S+;)/&amp;/gso;
+
+    # Convert Unicode characters to entities
+    $xml =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
+
+    $xml =~ s/[\x00-\x1f]//go;
+    $_TD->{new}{marc} = $xml;
+
+    return "MODIFY";
+}
+
+return;
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION public.naco_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
+
+    use strict;
+    use Unicode::Normalize;
+    use Encode;
+
+    my $str = shift;
+    my $sf = shift;
+
+    # Apply NACO normalization to input string; based on
+    # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
+    #
+    # Note that unlike a strict reading of the NACO normalization rules,
+    # output is returned as lowercase instead of uppercase for compatibility
+    # with previous versions of the Evergreen naco_normalize routine.
+
+    # Convert to upper-case first; even though final output will be lowercase, doing this will
+    # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
+    # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
+    $str = uc $str;
+
+    # remove non-filing strings
+    $str =~ s/\x{0098}.*?\x{009C}//g;
+
+    $str = NFKD($str);
+
+    # additional substitutions - 3.6.
+    $str =~ s/\x{00C6}/AE/g;
+    $str =~ s/\x{00DE}/TH/g;
+    $str =~ s/\x{0152}/OE/g;
+    $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}]['/DDOLl/d;
+
+    # transformations based on Unicode category codes
+    $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
+
+       if ($sf && $sf =~ /^a/o) {
+               my $commapos = index($str, ',');
+               if ($commapos > -1) {
+                       if ($commapos != length($str) - 1) {
+                $str =~ s/,/\x07/; # preserve first comma
+                       }
+               }
+       }
+
+    # since we've stripped out the control characters, we can now
+    # use a few as placeholders temporarily
+    $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
+    $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
+    $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
+
+    # decimal digits
+    $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
+
+    # intentionally skipping step 8 of the NACO algorithm; if the string
+    # gets normalized away, that's fine.
+
+    # leading and trailing spaces
+    $str =~ s/\s+/ /g;
+    $str =~ s/^\s+//;
+    $str =~ s/\s+$//g;
+
+    return lc $str;
+$func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
+
+-- Currently, the only difference from naco_normalize is that search_normalize
+-- turns apostrophes into spaces, while naco_normalize collapses them.
+CREATE OR REPLACE FUNCTION public.search_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
+
+    use strict;
+    use Unicode::Normalize;
+    use Encode;
+
+    my $str = shift;
+    my $sf = shift;
+
+    # Apply NACO normalization to input string; based on
+    # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
+    #
+    # Note that unlike a strict reading of the NACO normalization rules,
+    # output is returned as lowercase instead of uppercase for compatibility
+    # with previous versions of the Evergreen naco_normalize routine.
+
+    # Convert to upper-case first; even though final output will be lowercase, doing this will
+    # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
+    # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
+    $str = uc $str;
+
+    # remove non-filing strings
+    $str =~ s/\x{0098}.*?\x{009C}//g;
+
+    $str = NFKD($str);
+
+    # additional substitutions - 3.6.
+    $str =~ s/\x{00C6}/AE/g;
+    $str =~ s/\x{00DE}/TH/g;
+    $str =~ s/\x{0152}/OE/g;
+    $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}][/DDOLl/d;
+
+    # transformations based on Unicode category codes
+    $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
+
+       if ($sf && $sf =~ /^a/o) {
+               my $commapos = index($str, ',');
+               if ($commapos > -1) {
+                       if ($commapos != length($str) - 1) {
+                $str =~ s/,/\x07/; # preserve first comma
+                       }
+               }
+       }
+
+    # since we've stripped out the control characters, we can now
+    # use a few as placeholders temporarily
+    $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
+    $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
+    $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
+
+    # decimal digits
+    $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
+
+    # intentionally skipping step 8 of the NACO algorithm; if the string
+    # gets normalized away, that's fine.
+
+    # leading and trailing spaces
+    $str =~ s/\s+/ /g;
+    $str =~ s/^\s+//;
+    $str =~ s/\s+$//g;
+
+    return lc $str;
+$func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0852', :eg_version);
+
+CREATE OR REPLACE FUNCTION action.hold_copy_calculated_proximity(
+    ahr_id INT,
+    acp_id BIGINT,
+    copy_context_ou INT DEFAULT NULL
+    -- TODO maybe? hold_context_ou INT DEFAULT NULL.  This would optionally
+    -- support an "ahprox" measurement: adjust prox between copy circ lib and
+    -- hold request lib, but I'm unsure whether to use this theoretical
+    -- argument only in the baseline calculation or later in the other
+    -- queries in this function.
+) RETURNS NUMERIC AS $f$
+DECLARE
+    aoupa           actor.org_unit_proximity_adjustment%ROWTYPE;
+    ahr             action.hold_request%ROWTYPE;
+    acp             asset.copy%ROWTYPE;
+    acn             asset.call_number%ROWTYPE;
+    acl             asset.copy_location%ROWTYPE;
+    baseline_prox   NUMERIC;
+
+    icl_list        INT[];
+    iol_list        INT[];
+    isl_list        INT[];
+    hpl_list        INT[];
+    hrl_list        INT[];
+
+BEGIN
+
+    SELECT * INTO ahr FROM action.hold_request WHERE id = ahr_id;
+    SELECT * INTO acp FROM asset.copy WHERE id = acp_id;
+    SELECT * INTO acn FROM asset.call_number WHERE id = acp.call_number;
+    SELECT * INTO acl FROM asset.copy_location WHERE id = acp.location;
+
+    IF copy_context_ou IS NULL THEN
+        copy_context_ou := acp.circ_lib;
+    END IF;
+
+    -- First, gather the baseline proximity of "here" to pickup lib
+    SELECT prox INTO baseline_prox FROM actor.org_unit_proximity WHERE from_org = copy_context_ou AND to_org = ahr.pickup_lib;
+
+    -- Find any absolute adjustments, and set the baseline prox to that
+    SELECT  adj.* INTO aoupa
+      FROM  actor.org_unit_proximity_adjustment adj
+            LEFT JOIN actor.org_unit_ancestors_distance(copy_context_ou) acp_cl ON (acp_cl.id = adj.item_circ_lib)
+            LEFT JOIN actor.org_unit_ancestors_distance(acn.owning_lib) acn_ol ON (acn_ol.id = adj.item_owning_lib)
+            LEFT JOIN actor.org_unit_ancestors_distance(acl.owning_lib) acl_ol ON (acl_ol.id = adj.copy_location)
+            LEFT JOIN actor.org_unit_ancestors_distance(ahr.pickup_lib) ahr_pl ON (ahr_pl.id = adj.hold_pickup_lib)
+            LEFT JOIN actor.org_unit_ancestors_distance(ahr.request_lib) ahr_rl ON (ahr_rl.id = adj.hold_request_lib)
+      WHERE (adj.circ_mod IS NULL OR adj.circ_mod = acp.circ_modifier) AND
+            (adj.item_circ_lib IS NULL OR adj.item_circ_lib = acp_cl.id) AND
+            (adj.item_owning_lib IS NULL OR adj.item_owning_lib = acn_ol.id) AND
+            (adj.copy_location IS NULL OR adj.copy_location = acl_ol.id) AND
+            (adj.hold_pickup_lib IS NULL OR adj.hold_pickup_lib = ahr_pl.id) AND
+            (adj.hold_request_lib IS NULL OR adj.hold_request_lib = ahr_rl.id) AND
+            absolute_adjustment AND
+            COALESCE(acp_cl.id, acn_ol.id, acl_ol.id, ahr_pl.id, ahr_rl.id) IS NOT NULL
+      ORDER BY
+            COALESCE(acp_cl.distance,999)
+                + COALESCE(acn_ol.distance,999)
+                + COALESCE(acl_ol.distance,999)
+                + COALESCE(ahr_pl.distance,999)
+                + COALESCE(ahr_rl.distance,999),
+            adj.pos
+      LIMIT 1;
+
+    IF FOUND THEN
+        baseline_prox := aoupa.prox_adjustment;
+    END IF;
+
+    -- Now find any relative adjustments, and change the baseline prox based on them
+    FOR aoupa IN
+        SELECT  adj.* 
+          FROM  actor.org_unit_proximity_adjustment adj
+                LEFT JOIN actor.org_unit_ancestors_distance(copy_context_ou) acp_cl ON (acp_cl.id = adj.item_circ_lib)
+                LEFT JOIN actor.org_unit_ancestors_distance(acn.owning_lib) acn_ol ON (acn_ol.id = adj.item_owning_lib)
+                LEFT JOIN actor.org_unit_ancestors_distance(acl.owning_lib) acl_ol ON (acn_ol.id = adj.copy_location)
+                LEFT JOIN actor.org_unit_ancestors_distance(ahr.pickup_lib) ahr_pl ON (ahr_pl.id = adj.hold_pickup_lib)
+                LEFT JOIN actor.org_unit_ancestors_distance(ahr.request_lib) ahr_rl ON (ahr_rl.id = adj.hold_request_lib)
+          WHERE (adj.circ_mod IS NULL OR adj.circ_mod = acp.circ_modifier) AND
+                (adj.item_circ_lib IS NULL OR adj.item_circ_lib = acp_cl.id) AND
+                (adj.item_owning_lib IS NULL OR adj.item_owning_lib = acn_ol.id) AND
+                (adj.copy_location IS NULL OR adj.copy_location = acl_ol.id) AND
+                (adj.hold_pickup_lib IS NULL OR adj.hold_pickup_lib = ahr_pl.id) AND
+                (adj.hold_request_lib IS NULL OR adj.hold_request_lib = ahr_rl.id) AND
+                NOT absolute_adjustment AND
+                COALESCE(acp_cl.id, acn_ol.id, acl_ol.id, ahr_pl.id, ahr_rl.id) IS NOT NULL
+    LOOP
+        baseline_prox := baseline_prox + aoupa.prox_adjustment;
+    END LOOP;
+
+    RETURN baseline_prox;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0853', :eg_version);
+
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+( 'lib.prefer_external_url', 'lib',
+  'Use external "library information URL" in copy table, if available',
+  'If set to true, the library name in the copy details section will link to the URL associated with the "Library information URL" library setting rather than the library information page generated by Evergreen.',
+  'bool', null
+);
+
+SELECT evergreen.upgrade_deps_block_check('0854', :eg_version);
+
+INSERT INTO permission.perm_list ( id, code, description ) VALUES (
+    553,
+    'UPDATE_ORG_UNIT_SETTING.circ.min_item_price',
+    oils_i18n_gettext(
+        553,
+        'UPDATE_ORG_UNIT_SETTING.circ.min_item_price',
+        'ppl',
+        'description'
+    )
+), (
+       554,
+    'UPDATE_ORG_UNIT_SETTING.circ.max_item_price',
+    oils_i18n_gettext(
+        554,
+        'UPDATE_ORG_UNIT_SETTING.circ.max_item_price',
+        'ppl',
+        'description'
+    )
+);
+
+INSERT into config.org_unit_setting_type
+    ( name, grp, label, description, datatype, fm_class )
+VALUES (
+    'circ.min_item_price',
+       'finance',
+    oils_i18n_gettext(
+        'circ.min_item_price',
+        'Minimum Item Price',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'circ.min_item_price',
+        'When charging for lost items, charge this amount as a minimum.',
+        'coust', 'description'),
+    'currency',
+    NULL
+), (
+    'circ.max_item_price',
+    'finance',
+    oils_i18n_gettext(
+        'circ.max_item_price',
+        'Maximum Item Price',
+        'coust', 'label'),
+    oils_i18n_gettext(
+        'circ.max_item_price',
+        'When charging for lost items, limit the charge to this as a maximum.',
+        'coust', 'description'),
+    'currency',
+    NULL
+);
+
+-- Compiled list of all changed functions and views where we went from:
+--   array_accum() to array_agg()
+--   array_to_string(array_agg()) to string_agg()
+
+SELECT evergreen.upgrade_deps_block_check('0855', :eg_version);
+
+-- from 000.functions.general.sql
+
+CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_AGG(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
+
+
+-- from 002.functions.config.sql
+
+CREATE OR REPLACE FUNCTION public.extract_marc_field ( TEXT, BIGINT, TEXT, TEXT ) RETURNS TEXT AS $$
+    SELECT regexp_replace(string_agg(output,' '),$4,'','g') FROM oils_xpath_table('id', 'marc', $1, $3, 'id='||$2)x(id INT, output TEXT);
+$$ LANGUAGE SQL;
+
+
+-- from 011.schema.authority.sql
+
+CREATE OR REPLACE FUNCTION authority.axis_authority_tags(a TEXT) RETURNS INT[] AS $$
+    SELECT ARRAY_AGG(field) FROM authority.browse_axis_authority_field_map WHERE axis = $1;
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION authority.axis_authority_tags_refs(a TEXT) RETURNS INT[] AS $$
+    SELECT ARRAY_AGG(y) from (
+       SELECT  unnest(ARRAY_CAT(
+                 ARRAY[a.field],
+                 (SELECT ARRAY_AGG(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.field)
+             )) y
+       FROM  authority.browse_axis_authority_field_map a
+       WHERE axis = $1) x
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION authority.btag_authority_tags(btag TEXT) RETURNS INT[] AS $$
+    SELECT ARRAY_AGG(authority_field) FROM authority.control_set_bib_field WHERE tag = $1
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION authority.btag_authority_tags_refs(btag TEXT) RETURNS INT[] AS $$
+    SELECT ARRAY_AGG(y) from (
+        SELECT  unnest(ARRAY_CAT(
+                    ARRAY[a.authority_field],
+                    (SELECT ARRAY_AGG(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.authority_field)
+                )) y
+      FROM  authority.control_set_bib_field a
+      WHERE a.tag = $1) x
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION authority.atag_authority_tags(atag TEXT) RETURNS INT[] AS $$
+    SELECT ARRAY_AGG(id) FROM authority.control_set_authority_field WHERE tag = $1
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION authority.atag_authority_tags_refs(atag TEXT) RETURNS INT[] AS $$
+    SELECT ARRAY_AGG(y) from (
+        SELECT  unnest(ARRAY_CAT(
+                    ARRAY[a.id],
+                    (SELECT ARRAY_AGG(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.id)
+                )) y
+      FROM  authority.control_set_authority_field a
+      WHERE a.tag = $1) x
+$$ LANGUAGE SQL;
+
+
+-- from 012.schema.vandelay.sql
+
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
+DECLARE
+    transformed_xml TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_value      TEXT;
+    new_attrs       HSTORE := ''::HSTORE;
+    attr_def        config.record_attr_definition%ROWTYPE;
+BEGIN
+
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
+
+        IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+            SELECT  STRING_AGG(x.value, COALESCE(attr_def.joiner,' ')) INTO attr_value
+              FROM  vandelay.flatten_marc(xml) AS x
+              WHERE x.tag LIKE attr_def.tag
+                    AND CASE
+                        WHEN attr_def.sf_list IS NOT NULL
+                            THEN POSITION(x.subfield IN attr_def.sf_list) > 0
+                        ELSE TRUE
+                        END
+              GROUP BY x.tag
+              ORDER BY x.tag
+              LIMIT 1;
+
+        ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+            attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
+
+        ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+            SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+
+            -- See if we can skip the XSLT ... it's expensive
+            IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                -- Can't skip the transform
+                IF xfrm.xslt <> '---' THEN
+                    transformed_xml := oils_xslt_process(xml,xfrm.xslt);
+                ELSE
+                    transformed_xml := xml;
+                END IF;
+
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            IF xfrm.name IS NULL THEN
+                -- just grab the marcxml (empty) transform
+                SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
+
+        ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+            SELECT  m.value::TEXT INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(xml) v
+                    JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
+              WHERE v.subfield = attr_def.phys_char_sf
+              LIMIT 1; -- Just in case ...
+
+        END IF;
+
+        -- apply index normalizers to attr_value
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+              WHERE attr = attr_def.name
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_nullable( attr_value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO attr_value;
+
+        END LOOP;
+
+        -- Add the new value to the hstore
+        new_attrs := new_attrs || hstore( attr_def.name, attr_value );
+
+    END LOOP;
+
+    RETURN new_attrs;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
+    SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_AGG(name) FROM config.record_attr_definition));
+$_$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
+    match_set_id INTEGER, record_xml TEXT, bucket_id INTEGER 
+) RETURNS SETOF vandelay.match_set_test_result AS $$
+DECLARE
+    tags_rstore HSTORE;
+    svf_rstore  HSTORE;
+    coal        TEXT;
+    joins       TEXT;
+    query_      TEXT;
+    wq          TEXT;
+    qvalue      INTEGER;
+    rec         RECORD;
+BEGIN
+    tags_rstore := vandelay.flatten_marc_hstore(record_xml);
+    svf_rstore := vandelay.extract_rec_attrs(record_xml);
+
+    CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
+    CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
+
+    -- generate the where clause and return that directly (into wq), and as
+    -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
+    wq := vandelay.get_expr_from_match_set(match_set_id, tags_rstore);
+
+    query_ := 'SELECT DISTINCT(record), ';
+
+    -- qrows table is for the quality bits we add to the SELECT clause
+    SELECT STRING_AGG(
+        'COALESCE(n' || q::TEXT || '.quality, 0)', ' + '
+    ) INTO coal FROM _vandelay_tmp_qrows;
+
+    -- our query string so far is the SELECT clause and the inital FROM.
+    -- no JOINs yet nor the WHERE clause
+    query_ := query_ || coal || ' AS quality ' || E'\n';
+
+    -- jrows table is for the joins we must make (and the real text conditions)
+    SELECT STRING_AGG(j, E'\n') INTO joins
+        FROM _vandelay_tmp_jrows;
+
+    -- add those joins and the where clause to our query.
+    query_ := query_ || joins || E'\n';
+
+    -- join the record bucket
+    IF bucket_id IS NOT NULL THEN
+        query_ := query_ || 'JOIN container.biblio_record_entry_bucket_item ' ||
+            'brebi ON (brebi.target_biblio_record_entry = record ' ||
+            'AND brebi.bucket = ' || bucket_id || E')\n';
+    END IF;
+
+    query_ := query_ || 'JOIN biblio.record_entry bre ON (bre.id = record) ' || 'WHERE ' || wq || ' AND not bre.deleted';
+
+    -- this will return rows of record,quality
+    FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
+        RETURN NEXT rec;
+    END LOOP;
+
+    DROP TABLE _vandelay_tmp_qrows;
+    DROP TABLE _vandelay_tmp_jrows;
+    RETURN;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
+    record_xml TEXT
+) RETURNS HSTORE AS $func$
+BEGIN
+    RETURN (SELECT
+        HSTORE(
+            ARRAY_AGG(tag || (COALESCE(subfield, ''))),
+            ARRAY_AGG(value)
+        )
+        FROM (
+            SELECT  tag, subfield, ARRAY_AGG(value)::TEXT AS value
+              FROM  (SELECT tag,
+                            subfield,
+                            CASE WHEN tag = '020' THEN -- caseless -- isbn
+                                LOWER((REGEXP_MATCHES(value,$$^(\S{10,17})$$))[1] || '%')
+                            WHEN tag = '022' THEN -- caseless -- issn
+                                LOWER((REGEXP_MATCHES(value,$$^(\S{4}[- ]?\S{4})$$))[1] || '%')
+                            WHEN tag = '024' THEN -- caseless -- upc (other)
+                                LOWER(value || '%')
+                            ELSE
+                                value
+                            END AS value
+                      FROM  vandelay.flatten_marc(record_xml)) x
+                GROUP BY tag, subfield ORDER BY tag, subfield
+        ) subquery
+    );
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
+    node vandelay.match_set_point,
+    tags_rstore HSTORE
+) RETURNS TEXT AS $$
+DECLARE
+    q           TEXT;
+    i           INTEGER;
+    this_op     TEXT;
+    children    INTEGER[];
+    child       vandelay.match_set_point;
+BEGIN
+    SELECT ARRAY_AGG(id) INTO children FROM vandelay.match_set_point
+        WHERE parent = node.id;
+
+    IF ARRAY_LENGTH(children, 1) > 0 THEN
+        this_op := vandelay._get_expr_render_one(node);
+        q := '(';
+        i := 1;
+        WHILE children[i] IS NOT NULL LOOP
+            SELECT * INTO child FROM vandelay.match_set_point
+                WHERE id = children[i];
+            IF i > 1 THEN
+                q := q || ' ' || this_op || ' ';
+            END IF;
+            i := i + 1;
+            q := q || vandelay.get_expr_from_match_set_point(child, tags_rstore);
+        END LOOP;
+        q := q || ')';
+        RETURN q;
+    ELSIF node.bool_op IS NULL THEN
+        PERFORM vandelay._get_expr_push_qrow(node);
+        PERFORM vandelay._get_expr_push_jrow(node, tags_rstore);
+        RETURN vandelay._get_expr_render_one(node);
+    ELSE
+        RETURN '';
+    END IF;
+END;
+$$  LANGUAGE PLPGSQL;
+
+
+-- from 030.schema.metabib.sql
+
+CREATE OR REPLACE FUNCTION biblio.extract_located_uris( bib_id BIGINT, marcxml TEXT, editor_id INT ) RETURNS VOID AS $func$
+DECLARE
+    uris            TEXT[];
+    uri_xml         TEXT;
+    uri_label       TEXT;
+    uri_href        TEXT;
+    uri_use         TEXT;
+    uri_owner_list  TEXT[];
+    uri_owner       TEXT;
+    uri_owner_id    INT;
+    uri_id          INT;
+    uri_cn_id       INT;
+    uri_map_id      INT;
+BEGIN
+
+    -- Clear any URI mappings and call numbers for this bib.
+    -- This leads to acn / auricnm inflation, but also enables
+    -- old acn/auricnm's to go away and for bibs to be deleted.
+    FOR uri_cn_id IN SELECT id FROM asset.call_number WHERE record = bib_id AND label = '##URI##' AND NOT deleted LOOP
+        DELETE FROM asset.uri_call_number_map WHERE call_number = uri_cn_id;
+        DELETE FROM asset.call_number WHERE id = uri_cn_id;
+    END LOOP;
+
+    uris := oils_xpath('//*[@tag="856" and (@ind1="4" or @ind1="1") and (@ind2="0" or @ind2="1")]',marcxml);
+    IF ARRAY_UPPER(uris,1) > 0 THEN
+        FOR i IN 1 .. ARRAY_UPPER(uris, 1) LOOP
+            -- First we pull info out of the 856
+            uri_xml     := uris[i];
+
+            uri_href    := (oils_xpath('//*[@code="u"]/text()',uri_xml))[1];
+            uri_label   := (oils_xpath('//*[@code="y"]/text()|//*[@code="3"]/text()',uri_xml))[1];
+            uri_use     := (oils_xpath('//*[@code="z"]/text()|//*[@code="2"]/text()|//*[@code="n"]/text()',uri_xml))[1];
+
+            IF uri_label IS NULL THEN
+                uri_label := uri_href;
+            END IF;
+            CONTINUE WHEN uri_href IS NULL;
+
+            -- Get the distinct list of libraries wanting to use 
+            SELECT  ARRAY_AGG(
+                        DISTINCT REGEXP_REPLACE(
+                            x,
+                            $re$^.*?\((\w+)\).*$$re$,
+                            E'\\1'
+                        )
+                    ) INTO uri_owner_list
+              FROM  UNNEST(
+                        oils_xpath(
+                            '//*[@code="9"]/text()|//*[@code="w"]/text()|//*[@code="n"]/text()',
+                            uri_xml
+                        )
+                    )x;
+
+            IF ARRAY_UPPER(uri_owner_list,1) > 0 THEN
+
+                -- look for a matching uri
+                IF uri_use IS NULL THEN
+                    SELECT id INTO uri_id
+                        FROM asset.uri
+                        WHERE label = uri_label AND href = uri_href AND use_restriction IS NULL AND active
+                        ORDER BY id LIMIT 1;
+                    IF NOT FOUND THEN -- create one
+                        INSERT INTO asset.uri (label, href, use_restriction) VALUES (uri_label, uri_href, uri_use);
+                        SELECT id INTO uri_id
+                            FROM asset.uri
+                            WHERE label = uri_label AND href = uri_href AND use_restriction IS NULL AND active;
+                    END IF;
+                ELSE
+                    SELECT id INTO uri_id
+                        FROM asset.uri
+                        WHERE label = uri_label AND href = uri_href AND use_restriction = uri_use AND active
+                        ORDER BY id LIMIT 1;
+                    IF NOT FOUND THEN -- create one
+                        INSERT INTO asset.uri (label, href, use_restriction) VALUES (uri_label, uri_href, uri_use);
+                        SELECT id INTO uri_id
+                            FROM asset.uri
+                            WHERE label = uri_label AND href = uri_href AND use_restriction = uri_use AND active;
+                    END IF;
+                END IF;
+
+                FOR j IN 1 .. ARRAY_UPPER(uri_owner_list, 1) LOOP
+                    uri_owner := uri_owner_list[j];
+
+                    SELECT id INTO uri_owner_id FROM actor.org_unit WHERE shortname = uri_owner;
+                    CONTINUE WHEN NOT FOUND;
+
+                    -- we need a call number to link through
+                    SELECT id INTO uri_cn_id FROM asset.call_number WHERE owning_lib = uri_owner_id AND record = bib_id AND label = '##URI##' AND NOT deleted;
+                    IF NOT FOUND THEN
+                        INSERT INTO asset.call_number (owning_lib, record, create_date, edit_date, creator, editor, label)
+                            VALUES (uri_owner_id, bib_id, 'now', 'now', editor_id, editor_id, '##URI##');
+                        SELECT id INTO uri_cn_id FROM asset.call_number WHERE owning_lib = uri_owner_id AND record = bib_id AND label = '##URI##' AND NOT deleted;
+                    END IF;
+
+                    -- now, link them if they're not already
+                    SELECT id INTO uri_map_id FROM asset.uri_call_number_map WHERE call_number = uri_cn_id AND uri = uri_id;
+                    IF NOT FOUND THEN
+                        INSERT INTO asset.uri_call_number_map (call_number, uri) VALUES (uri_cn_id, uri_id);
+                    END IF;
+
+                END LOOP;
+
+            END IF;
+
+        END LOOP;
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+DECLARE
+    transformed_xml TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_value      TEXT;
+    new_attrs       HSTORE := ''::HSTORE;
+    attr_def        config.record_attr_definition%ROWTYPE;
+BEGIN
+
+    IF NEW.deleted IS TRUE THEN -- If this bib is deleted
+        PERFORM * FROM config.internal_flag WHERE
+            name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled;
+        IF NOT FOUND THEN
+            -- One needs to keep these around to support searches
+            -- with the #deleted modifier, so one should turn on the named
+            -- internal flag for that functionality.
+            DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id;
+            DELETE FROM metabib.record_attr WHERE id = NEW.id;
+        END IF;
+
+        DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
+        DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
+        RETURN NEW; -- and we're done
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+    END IF;
+
+    -- Record authority linking
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
+    END IF;
+
+    -- Flatten and insert the mfr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM metabib.reingest_metabib_full_rec(NEW.id);
+
+        -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            FOR attr_def IN SELECT * FROM config.record_attr_definition ORDER BY format LOOP
+
+                IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+                    SELECT  STRING_AGG(value, COALESCE(attr_def.joiner,' ')) INTO attr_value
+                      FROM  (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
+                      WHERE record = NEW.id
+                            AND tag LIKE attr_def.tag
+                            AND CASE
+                                WHEN attr_def.sf_list IS NOT NULL 
+                                    THEN POSITION(subfield IN attr_def.sf_list) > 0
+                                ELSE TRUE
+                                END
+                      GROUP BY tag
+                      ORDER BY tag
+                      LIMIT 1;
+
+                ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+                    attr_value := biblio.marc21_extract_fixed_field(NEW.id, attr_def.fixed_field);
+
+                ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+                    SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+            
+                    -- See if we can skip the XSLT ... it's expensive
+                    IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                        -- Can't skip the transform
+                        IF xfrm.xslt <> '---' THEN
+                            transformed_xml := oils_xslt_process(NEW.marc,xfrm.xslt);
+                        ELSE
+                            transformed_xml := NEW.marc;
+                        END IF;
+            
+                        prev_xfrm := xfrm.name;
+                    END IF;
+
+                    IF xfrm.name IS NULL THEN
+                        -- just grab the marcxml (empty) transform
+                        SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                        prev_xfrm := xfrm.name;
+                    END IF;
+
+                    attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
+
+                ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+                    SELECT  m.value INTO attr_value
+                      FROM  biblio.marc21_physical_characteristics(NEW.id) v
+                            JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
+                      WHERE v.subfield = attr_def.phys_char_sf
+                      LIMIT 1; -- Just in case ...
+
+                END IF;
+
+                -- apply index normalizers to attr_value
+                FOR normalizer IN
+                    SELECT  n.func AS func,
+                            n.param_count AS param_count,
+                            m.params AS params
+                      FROM  config.index_normalizer n
+                            JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+                      WHERE attr = attr_def.name
+                      ORDER BY m.pos LOOP
+                        EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                            COALESCE( quote_literal( attr_value ), 'NULL' ) ||
+                            CASE
+                                WHEN normalizer.param_count > 0
+                                    THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                                    ELSE ''
+                                END ||
+                            ')' INTO attr_value;
+        
+                END LOOP;
+
+                -- Add the new value to the hstore
+                new_attrs := new_attrs || hstore( attr_def.name, attr_value );
+
+            END LOOP;
+
+            IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
+                DELETE FROM metabib.record_attr WHERE id = NEW.id;
+                INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
+            ELSE
+                UPDATE metabib.record_attr SET attrs = new_attrs WHERE id = NEW.id;
+            END IF;
+
+        END IF;
+    END IF;
+
+    -- Gather and insert the field entry data
+    PERFORM metabib.reingest_metabib_field_entries(NEW.id);
+
+    -- Located URI magic
+    IF TG_OP = 'INSERT' THEN
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    ELSE
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    END IF;
+
+    -- (re)map metarecord-bib linking
+    IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
+        IF NOT FOUND THEN
+            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 * 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;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+-- from 100.circ_matrix.sql
+
+CREATE OR REPLACE FUNCTION actor.calculate_system_penalties( match_user INT, context_org INT ) RETURNS SETOF actor.usr_standing_penalty AS $func$
+DECLARE
+    user_object         actor.usr%ROWTYPE;
+    new_sp_row          actor.usr_standing_penalty%ROWTYPE;
+    existing_sp_row     actor.usr_standing_penalty%ROWTYPE;
+    collections_fines   permission.grp_penalty_threshold%ROWTYPE;
+    max_fines           permission.grp_penalty_threshold%ROWTYPE;
+    max_overdue         permission.grp_penalty_threshold%ROWTYPE;
+    max_items_out       permission.grp_penalty_threshold%ROWTYPE;
+    max_lost            permission.grp_penalty_threshold%ROWTYPE;
+    max_longoverdue     permission.grp_penalty_threshold%ROWTYPE;
+    tmp_grp             INT;
+    items_overdue       INT;
+    items_out           INT;
+    items_lost          INT;
+    items_longoverdue   INT;
+    context_org_list    INT[];
+    current_fines        NUMERIC(8,2) := 0.0;
+    tmp_fines            NUMERIC(8,2);
+    tmp_groc            RECORD;
+    tmp_circ            RECORD;
+    tmp_org             actor.org_unit%ROWTYPE;
+    tmp_penalty         config.standing_penalty%ROWTYPE;
+    tmp_depth           INTEGER;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+
+    -- Max fines
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has a high fine balance
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 1 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_fines.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 1;
+
+        SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                      FROM  booking.reservation r
+                      WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                      FROM  money.grocery g
+                      WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                      FROM  action.circulation circ
+                      WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND xact_finish IS NULL ) l USING (id);
+
+        IF current_fines >= max_fines.threshold THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_fines.org_unit;
+            new_sp_row.standing_penalty := 1;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max overdue
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many overdue items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+
+            SELECT * INTO max_overdue FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 2 AND org_unit = tmp_org.id;
+
+            IF max_overdue.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_overdue.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_overdue.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_overdue.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 2;
+
+        SELECT  INTO items_overdue COUNT(*)
+          FROM  action.circulation circ
+                JOIN  actor.org_unit_full_path( max_overdue.org_unit ) fp ON (circ.circ_lib = fp.id)
+          WHERE circ.usr = match_user
+            AND circ.checkin_time IS NULL
+            AND circ.due_date < NOW()
+            AND (circ.stop_fines = 'MAXFINES' OR circ.stop_fines IS NULL);
+
+        IF items_overdue >= max_overdue.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_overdue.org_unit;
+            new_sp_row.standing_penalty := 2;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max out
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many checked out items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_items_out FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 3 AND org_unit = tmp_org.id;
+
+            IF max_items_out.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_items_out.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+
+    -- Fail if the user has too many items checked out
+    IF max_items_out.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_items_out.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 3;
+
+        SELECT  INTO items_out COUNT(*)
+          FROM  action.circulation circ
+                JOIN  actor.org_unit_full_path( max_items_out.org_unit ) fp ON (circ.circ_lib = fp.id)
+          WHERE circ.usr = match_user
+                AND circ.checkin_time IS NULL
+                AND (circ.stop_fines IN (
+                    SELECT 'MAXFINES'::TEXT
+                    UNION ALL
+                    SELECT 'LONGOVERDUE'::TEXT
+                    UNION ALL
+                    SELECT 'LOST'::TEXT
+                    WHERE 'true' ILIKE
+                    (
+                        SELECT CASE
+                            WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.tally_lost', circ.circ_lib)) ILIKE 'true' THEN 'true'
+                            ELSE 'false'
+                        END
+                    )
+                    UNION ALL
+                    SELECT 'CLAIMSRETURNED'::TEXT
+                    WHERE 'false' ILIKE
+                    (
+                        SELECT CASE
+                            WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.do_not_tally_claims_returned', circ.circ_lib)) ILIKE 'true' THEN 'true'
+                            ELSE 'false'
+                        END
+                    )
+                    ) OR circ.stop_fines IS NULL)
+                AND xact_finish IS NULL;
+
+           IF items_out >= max_items_out.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_items_out.org_unit;
+            new_sp_row.standing_penalty := 3;
+            RETURN NEXT new_sp_row;
+           END IF;
+    END IF;
+
+    -- Start over for max lost
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many lost items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+
+            SELECT * INTO max_lost FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 5 AND org_unit = tmp_org.id;
+
+            IF max_lost.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_lost.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_lost.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+            FROM  actor.usr_standing_penalty
+            WHERE usr = match_user
+                AND org_unit = max_lost.org_unit
+                AND (stop_date IS NULL or stop_date > NOW())
+                AND standing_penalty = 5;
+
+        SELECT  INTO items_lost COUNT(*)
+        FROM  action.circulation circ
+            JOIN  actor.org_unit_full_path( max_lost.org_unit ) fp ON (circ.circ_lib = fp.id)
+        WHERE circ.usr = match_user
+            AND circ.checkin_time IS NULL
+            AND (circ.stop_fines = 'LOST')
+            AND xact_finish IS NULL;
+
+        IF items_lost >= max_lost.threshold::INT AND 0 < max_lost.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_lost.org_unit;
+            new_sp_row.standing_penalty := 5;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for max longoverdue
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has too many longoverdue items
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+
+            SELECT * INTO max_longoverdue 
+                FROM permission.grp_penalty_threshold 
+                WHERE grp = tmp_grp AND 
+                    penalty = 35 AND 
+                    org_unit = tmp_org.id;
+
+            IF max_longoverdue.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp 
+                    FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_longoverdue.threshold IS NOT NULL 
+                OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_longoverdue.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+            FROM  actor.usr_standing_penalty
+            WHERE usr = match_user
+                AND org_unit = max_longoverdue.org_unit
+                AND (stop_date IS NULL or stop_date > NOW())
+                AND standing_penalty = 35;
+
+        SELECT INTO items_longoverdue COUNT(*)
+        FROM action.circulation circ
+            JOIN actor.org_unit_full_path( max_longoverdue.org_unit ) fp 
+                ON (circ.circ_lib = fp.id)
+        WHERE circ.usr = match_user
+            AND circ.checkin_time IS NULL
+            AND (circ.stop_fines = 'LONGOVERDUE')
+            AND xact_finish IS NULL;
+
+        IF items_longoverdue >= max_longoverdue.threshold::INT 
+                AND 0 < max_longoverdue.threshold::INT THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_longoverdue.org_unit;
+            new_sp_row.standing_penalty := 35;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+
+    -- Start over for collections warning
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Fail if the user has a collections-level fine balance
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 4 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        RETURN QUERY
+            SELECT  *
+              FROM  actor.usr_standing_penalty
+              WHERE usr = match_user
+                    AND org_unit = max_fines.org_unit
+                    AND (stop_date IS NULL or stop_date > NOW())
+                    AND standing_penalty = 4;
+
+        SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                      FROM  booking.reservation r
+                      WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND r.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                      FROM  money.grocery g
+                      WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND g.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                      FROM  action.circulation circ
+                      WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND circ.xact_finish IS NULL ) l USING (id);
+
+        IF current_fines >= max_fines.threshold THEN
+            new_sp_row.usr := match_user;
+            new_sp_row.org_unit := max_fines.org_unit;
+            new_sp_row.standing_penalty := 4;
+            RETURN NEXT new_sp_row;
+        END IF;
+    END IF;
+
+    -- Start over for in collections
+    SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+    -- Remove the in-collections penalty if the user has paid down enough
+    -- This penalty is different, because this code is not responsible for creating 
+    -- new in-collections penalties, only for removing them
+    LOOP
+        tmp_grp := user_object.profile;
+        LOOP
+            SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 30 AND org_unit = tmp_org.id;
+
+            IF max_fines.threshold IS NULL THEN
+                SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
+            ELSE
+                EXIT;
+            END IF;
+
+            IF tmp_grp IS NULL THEN
+                EXIT;
+            END IF;
+        END LOOP;
+
+        IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
+            EXIT;
+        END IF;
+
+        SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+
+    END LOOP;
+
+    IF max_fines.threshold IS NOT NULL THEN
+
+        SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+        -- first, see if the user had paid down to the threshold
+        SELECT  SUM(f.balance_owed) INTO current_fines
+          FROM  money.materialized_billable_xact_summary f
+                JOIN (
+                    SELECT  r.id
+                      FROM  booking.reservation r
+                      WHERE r.usr = match_user
+                            AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND r.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  g.id
+                      FROM  money.grocery g
+                      WHERE g.usr = match_user
+                            AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+                            AND g.xact_finish IS NULL
+                                UNION ALL
+                    SELECT  circ.id
+                      FROM  action.circulation circ
+                      WHERE circ.usr = match_user
+                            AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+                            AND circ.xact_finish IS NULL ) l USING (id);
+
+        IF current_fines IS NULL OR current_fines <= max_fines.threshold THEN
+            -- patron has paid down enough
+
+            SELECT INTO tmp_penalty * FROM config.standing_penalty WHERE id = 30;
+
+            IF tmp_penalty.org_depth IS NOT NULL THEN
+
+                -- since this code is not responsible for applying the penalty, it can't 
+                -- guarantee the current context org will match the org at which the penalty 
+                --- was applied.  search up the org tree until we hit the configured penalty depth
+                SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+                SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+
+                WHILE tmp_depth >= tmp_penalty.org_depth LOOP
+
+                    RETURN QUERY
+                        SELECT  *
+                          FROM  actor.usr_standing_penalty
+                          WHERE usr = match_user
+                                AND org_unit = tmp_org.id
+                                AND (stop_date IS NULL or stop_date > NOW())
+                                AND standing_penalty = 30;
+
+                    IF tmp_org.parent_ou IS NULL THEN
+                        EXIT;
+                    END IF;
+
+                    SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+                    SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+                END LOOP;
+
+            ELSE
+
+                -- no penalty depth is defined, look for exact matches
+
+                RETURN QUERY
+                    SELECT  *
+                      FROM  actor.usr_standing_penalty
+                      WHERE usr = match_user
+                            AND org_unit = max_fines.org_unit
+                            AND (stop_date IS NULL or stop_date > NOW())
+                            AND standing_penalty = 30;
+            END IF;
+    
+        END IF;
+
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE plpgsql;
+
+
+-- from 110.hold_matrix.sql
+
+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;
+    item_cn_object     asset.call_number%ROWTYPE;
+    item_status_object  config.copy_status%ROWTYPE;
+    item_location_object    asset.copy_location%ROWTYPE;
+    ou_skip              actor.org_unit_setting%ROWTYPE;
+    result            action.matrix_test_result;
+    hold_test        config.hold_matrix_matchpoint%ROWTYPE;
+    use_active_date   TEXT;
+    age_protect_date  TIMESTAMP WITH TIME ZONE;
+    hold_count        INT;
+    hold_transit_prox    INT;
+    frozen_hold_count    INT;
+    context_org_list    INT[];
+    done            BOOL := FALSE;
+    hold_penalty TEXT;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+    SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( pickup_ou );
+
+    result.success := TRUE;
+
+    -- The HOLD penalty block only applies to new holds.
+    -- The CAPTURE penalty block applies to existing holds.
+    hold_penalty := 'HOLD';
+    IF retargetting THEN
+        hold_penalty := 'CAPTURE';
+    END IF;
+
+    -- 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;
+
+    SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+    SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status;
+    SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
+
+    -- 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 item_object.holdable IS FALSE THEN
+        result.fail_part := 'item.holdable';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+    END IF;
+
+    IF item_status_object.holdable IS FALSE THEN
+        result.fail_part := 'status.holdable';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+    END IF;
+
+    IF item_location_object.holdable IS FALSE THEN
+        result.fail_part := 'location.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 unnest(context_org_list) )
+                AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+                AND csp.block_list LIKE '%' || hold_penalty || '%' 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 unnest(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 hold_test.distance_is_from_owner THEN
+            SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
+        ELSE
+            SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
+        END IF;
+        IF use_active_date = 'true' THEN
+            age_protect_date := COALESCE(item_object.active_date, NOW());
+        ELSE
+            age_protect_date := item_object.create_date;
+        END IF;
+        IF age_protect_date + age_protect_object.age > NOW() THEN
+            IF hold_test.distance_is_from_owner THEN
+                SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+                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;
+
+
+-- from 300.schema.staged_search.sql
+
+CREATE OR REPLACE FUNCTION search.query_parser_fts (
+
+    param_search_ou INT,
+    param_depth     INT,
+    param_query     TEXT,
+    param_statuses  INT[],
+    param_locations INT[],
+    param_offset    INT,
+    param_check     INT,
+    param_limit     INT,
+    metarecord      BOOL,
+    staff           BOOL,
+    deleted_search  BOOL,
+    param_pref_ou   INT DEFAULT NULL
+) RETURNS SETOF search.search_result AS $func$
+DECLARE
+
+    current_res         search.search_result%ROWTYPE;
+    search_org_list     INT[];
+    luri_org_list       INT[];
+    tmp_int_list        INT[];
+
+    check_limit         INT;
+    core_limit          INT;
+    core_offset         INT;
+    tmp_int             INT;
+
+    core_result         RECORD;
+    core_cursor         REFCURSOR;
+    core_rel_query      TEXT;
+
+    total_count         INT := 0;
+    check_count         INT := 0;
+    deleted_count       INT := 0;
+    visible_count       INT := 0;
+    excluded_count      INT := 0;
+
+BEGIN
+
+    check_limit := COALESCE( param_check, 1000 );
+    core_limit  := COALESCE( param_limit, 25000 );
+    core_offset := COALESCE( param_offset, 0 );
+
+    -- core_skip_chk := COALESCE( param_skip_chk, 1 );
+
+    IF param_search_ou > 0 THEN
+        IF param_depth IS NOT NULL THEN
+            SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
+        ELSE
+            SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
+        END IF;
+
+        SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
+
+    ELSIF param_search_ou < 0 THEN
+        SELECT ARRAY_AGG(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
+
+        FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
+            SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
+            luri_org_list := luri_org_list || tmp_int_list;
+        END LOOP;
+
+        SELECT ARRAY_AGG(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
+
+    ELSIF param_search_ou = 0 THEN
+        -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
+    END IF;
+
+    IF param_pref_ou IS NOT NULL THEN
+        SELECT array_agg(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors(param_pref_ou);
+        luri_org_list := luri_org_list || tmp_int_list;
+    END IF;
+
+    OPEN core_cursor FOR EXECUTE param_query;
+
+    LOOP
+
+        FETCH core_cursor INTO core_result;
+        EXIT WHEN NOT FOUND;
+        EXIT WHEN total_count >= core_limit;
+
+        total_count := total_count + 1;
+
+        CONTINUE WHEN total_count NOT BETWEEN  core_offset + 1 AND check_limit + core_offset;
+
+        check_count := check_count + 1;
+
+        IF NOT deleted_search THEN
+
+            PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+            IF NOT FOUND THEN
+                -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
+                deleted_count := deleted_count + 1;
+                CONTINUE;
+            END IF;
+
+            PERFORM 1
+              FROM  biblio.record_entry b
+                    JOIN config.bib_source s ON (b.source = s.id)
+              WHERE s.transcendant
+                    AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+
+            IF FOUND THEN
+                -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
+                visible_count := visible_count + 1;
+
+                current_res.id = core_result.id;
+                current_res.rel = core_result.rel;
+
+                tmp_int := 1;
+                IF metarecord THEN
+                    SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+                END IF;
+
+                IF tmp_int = 1 THEN
+                    current_res.record = core_result.records[1];
+                ELSE
+                    current_res.record = NULL;
+                END IF;
+
+                RETURN NEXT current_res;
+
+                CONTINUE;
+            END IF;
+
+            PERFORM 1
+              FROM  asset.call_number cn
+                    JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
+                    JOIN asset.uri uri ON (map.uri = uri.id)
+              WHERE NOT cn.deleted
+                    AND cn.label = '##URI##'
+                    AND uri.active
+                    AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
+                    AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                    AND cn.owning_lib IN ( SELECT * FROM unnest( luri_org_list ) )
+              LIMIT 1;
+
+            IF FOUND THEN
+                -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
+                visible_count := visible_count + 1;
+
+                current_res.id = core_result.id;
+                current_res.rel = core_result.rel;
+
+                tmp_int := 1;
+                IF metarecord THEN
+                    SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+                END IF;
+
+                IF tmp_int = 1 THEN
+                    current_res.record = core_result.records[1];
+                ELSE
+                    current_res.record = NULL;
+                END IF;
+
+                RETURN NEXT current_res;
+
+                CONTINUE;
+            END IF;
+
+            IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+                    -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            END IF;
+
+            IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+                        -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            END IF;
+
+            IF staff IS NULL OR NOT staff THEN
+
+                PERFORM 1
+                  FROM  asset.opac_visible_copies
+                  WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy)
+                      WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+
+                        -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            ELSE
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+
+                        PERFORM 1
+                          FROM  asset.call_number cn
+                                JOIN asset.copy cp ON (cp.call_number = cn.id)
+                          WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                                AND NOT cp.deleted
+                          LIMIT 1;
+
+                        IF FOUND THEN
+                            -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                            excluded_count := excluded_count + 1;
+                            CONTINUE;
+                        END IF;
+                    END IF;
+
+                END IF;
+
+            END IF;
+
+        END IF;
+
+        visible_count := visible_count + 1;
+
+        current_res.id = core_result.id;
+        current_res.rel = core_result.rel;
+
+        tmp_int := 1;
+        IF metarecord THEN
+            SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+        END IF;
+
+        IF tmp_int = 1 THEN
+            current_res.record = core_result.records[1];
+        ELSE
+            current_res.record = NULL;
+        END IF;
+
+        RETURN NEXT current_res;
+
+        IF visible_count % 1000 = 0 THEN
+            -- RAISE NOTICE ' % visible so far ... ', visible_count;
+        END IF;
+
+    END LOOP;
+
+    current_res.id = NULL;
+    current_res.rel = NULL;
+    current_res.record = NULL;
+    current_res.total = total_count;
+    current_res.checked = check_count;
+    current_res.deleted = deleted_count;
+    current_res.visible = visible_count;
+    current_res.excluded = excluded_count;
+
+    CLOSE core_cursor;
+
+    RETURN NEXT current_res;
+
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+-- from 990.schema.unapi.sql
+
+CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
+RETURNS anyarray AS $$
+    SELECT ARRAY_AGG(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
+$$ LANGUAGE SQL STABLE;
+
+
+-- from 999.functions.global.sql
+
+CREATE OR REPLACE FUNCTION asset.merge_record_assets( target_record BIGINT, source_record BIGINT ) RETURNS INT AS $func$
+DECLARE
+    moved_objects INT := 0;
+    source_cn     asset.call_number%ROWTYPE;
+    target_cn     asset.call_number%ROWTYPE;
+    metarec       metabib.metarecord%ROWTYPE;
+    hold          action.hold_request%ROWTYPE;
+    ser_rec       serial.record_entry%ROWTYPE;
+    ser_sub       serial.subscription%ROWTYPE;
+    acq_lineitem  acq.lineitem%ROWTYPE;
+    acq_request   acq.user_request%ROWTYPE;
+    booking       booking.resource_type%ROWTYPE;
+    source_part   biblio.monograph_part%ROWTYPE;
+    target_part   biblio.monograph_part%ROWTYPE;
+    multi_home    biblio.peer_bib_copy_map%ROWTYPE;
+    uri_count     INT := 0;
+    counter       INT := 0;
+    uri_datafield TEXT;
+    uri_text      TEXT := '';
+BEGIN
+
+    -- move any 856 entries on records that have at least one MARC-mapped URI entry
+    SELECT  INTO uri_count COUNT(*)
+      FROM  asset.uri_call_number_map m
+            JOIN asset.call_number cn ON (m.call_number = cn.id)
+      WHERE cn.record = source_record;
+
+    IF uri_count > 0 THEN
+        
+        -- This returns more nodes than you might expect:
+        -- 7 instead of 1 for an 856 with $u $y $9
+        SELECT  COUNT(*) INTO counter
+          FROM  oils_xpath_table(
+                    'id',
+                    'marc',
+                    'biblio.record_entry',
+                    '//*[@tag="856"]',
+                    'id=' || source_record
+                ) as t(i int,c text);
+    
+        FOR i IN 1 .. counter LOOP
+            SELECT  '<datafield xmlns="http://www.loc.gov/MARC21/slim"' || 
+                       ' tag="856"' ||
+                       ' ind1="' || FIRST(ind1) || '"'  ||
+                       ' ind2="' || FIRST(ind2) || '">' ||
+                        STRING_AGG(
+                            '<subfield code="' || subfield || '">' ||
+                            regexp_replace(
+                                regexp_replace(
+                                    regexp_replace(data,'&','&amp;','g'),
+                                    '>', '&gt;', 'g'
+                                ),
+                                '<', '&lt;', 'g'
+                            ) || '</subfield>', ''
+                        ) || '</datafield>' INTO uri_datafield
+              FROM  oils_xpath_table(
+                        'id',
+                        'marc',
+                        'biblio.record_entry',
+                        '//*[@tag="856"][position()=' || i || ']/@ind1|' ||
+                        '//*[@tag="856"][position()=' || i || ']/@ind2|' ||
+                        '//*[@tag="856"][position()=' || i || ']/*/@code|' ||
+                        '//*[@tag="856"][position()=' || i || ']/*[@code]',
+                        'id=' || source_record
+                    ) as t(id int,ind1 text, ind2 text,subfield text,data text);
+
+            -- As most of the results will be NULL, protect against NULLifying
+            -- the valid content that we do generate
+            uri_text := uri_text || COALESCE(uri_datafield, '');
+        END LOOP;
+
+        IF uri_text <> '' THEN
+            UPDATE  biblio.record_entry
+              SET   marc = regexp_replace(marc,'(</[^>]*record>)', uri_text || E'\\1')
+              WHERE id = target_record;
+        END IF;
+
+    END IF;
+
+       -- Find and move metarecords to the target record
+       SELECT  INTO metarec *
+         FROM  metabib.metarecord
+         WHERE master_record = source_record;
+
+       IF FOUND THEN
+               UPDATE  metabib.metarecord
+                 SET   master_record = target_record,
+                       mods = NULL
+                 WHERE id = metarec.id;
+
+               moved_objects := moved_objects + 1;
+       END IF;
+
+       -- Find call numbers attached to the source ...
+       FOR source_cn IN SELECT * FROM asset.call_number WHERE record = source_record LOOP
+
+               SELECT  INTO target_cn *
+                 FROM  asset.call_number
+                 WHERE label = source_cn.label
+                       AND owning_lib = source_cn.owning_lib
+                       AND record = target_record
+                       AND NOT deleted;
+
+               -- ... and if there's a conflicting one on the target ...
+               IF FOUND THEN
+
+                       -- ... move the copies to that, and ...
+                       UPDATE  asset.copy
+                         SET   call_number = target_cn.id
+                         WHERE call_number = source_cn.id;
+
+                       -- ... move V holds to the move-target call number
+                       FOR hold IN SELECT * FROM action.hold_request WHERE target = source_cn.id AND hold_type = 'V' LOOP
+               
+                               UPDATE  action.hold_request
+                                 SET   target = target_cn.id
+                                 WHERE id = hold.id;
+               
+                               moved_objects := moved_objects + 1;
+                       END LOOP;
+
+               -- ... if not ...
+               ELSE
+                       -- ... just move the call number to the target record
+                       UPDATE  asset.call_number
+                         SET   record = target_record
+                         WHERE id = source_cn.id;
+               END IF;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find T holds targeting the source record ...
+       FOR hold IN SELECT * FROM action.hold_request WHERE target = source_record AND hold_type = 'T' LOOP
+
+               -- ... and move them to the target record
+               UPDATE  action.hold_request
+                 SET   target = target_record
+                 WHERE id = hold.id;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find serial records targeting the source record ...
+       FOR ser_rec IN SELECT * FROM serial.record_entry WHERE record = source_record LOOP
+               -- ... and move them to the target record
+               UPDATE  serial.record_entry
+                 SET   record = target_record
+                 WHERE id = ser_rec.id;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find serial subscriptions targeting the source record ...
+       FOR ser_sub IN SELECT * FROM serial.subscription WHERE record_entry = source_record LOOP
+               -- ... and move them to the target record
+               UPDATE  serial.subscription
+                 SET   record_entry = target_record
+                 WHERE id = ser_sub.id;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find booking resource types targeting the source record ...
+       FOR booking IN SELECT * FROM booking.resource_type WHERE record = source_record LOOP
+               -- ... and move them to the target record
+               UPDATE  booking.resource_type
+                 SET   record = target_record
+                 WHERE id = booking.id;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find acq lineitems targeting the source record ...
+       FOR acq_lineitem IN SELECT * FROM acq.lineitem WHERE eg_bib_id = source_record LOOP
+               -- ... and move them to the target record
+               UPDATE  acq.lineitem
+                 SET   eg_bib_id = target_record
+                 WHERE id = acq_lineitem.id;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find acq user purchase requests targeting the source record ...
+       FOR acq_request IN SELECT * FROM acq.user_request WHERE eg_bib = source_record LOOP
+               -- ... and move them to the target record
+               UPDATE  acq.user_request
+                 SET   eg_bib = target_record
+                 WHERE id = acq_request.id;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find parts attached to the source ...
+       FOR source_part IN SELECT * FROM biblio.monograph_part WHERE record = source_record LOOP
+
+               SELECT  INTO target_part *
+                 FROM  biblio.monograph_part
+                 WHERE label = source_part.label
+                       AND record = target_record;
+
+               -- ... and if there's a conflicting one on the target ...
+               IF FOUND THEN
+
+                       -- ... move the copy-part maps to that, and ...
+                       UPDATE  asset.copy_part_map
+                         SET   part = target_part.id
+                         WHERE part = source_part.id;
+
+                       -- ... move P holds to the move-target part
+                       FOR hold IN SELECT * FROM action.hold_request WHERE target = source_part.id AND hold_type = 'P' LOOP
+               
+                               UPDATE  action.hold_request
+                                 SET   target = target_part.id
+                                 WHERE id = hold.id;
+               
+                               moved_objects := moved_objects + 1;
+                       END LOOP;
+
+               -- ... if not ...
+               ELSE
+                       -- ... just move the part to the target record
+                       UPDATE  biblio.monograph_part
+                         SET   record = target_record
+                         WHERE id = source_part.id;
+               END IF;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- Find multi_home items attached to the source ...
+       FOR multi_home IN SELECT * FROM biblio.peer_bib_copy_map WHERE peer_record = source_record LOOP
+               -- ... and move them to the target record
+               UPDATE  biblio.peer_bib_copy_map
+                 SET   peer_record = target_record
+                 WHERE id = multi_home.id;
+
+               moved_objects := moved_objects + 1;
+       END LOOP;
+
+       -- And delete mappings where the item's home bib was merged with the peer bib
+       DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = (
+               SELECT (SELECT record FROM asset.call_number WHERE id = call_number)
+               FROM asset.copy WHERE id = target_copy
+       );
+
+    -- Finally, "delete" the source record
+    DELETE FROM biblio.record_entry WHERE id = source_record;
+
+       -- That's all, folks!
+       RETURN moved_objects;
+END;
+$func$ LANGUAGE plpgsql;
+
+-- from reporter-schema.sql
+
+CREATE OR REPLACE VIEW reporter.simple_record AS
+SELECT r.id,
+       s.metarecord,
+       r.fingerprint,
+       r.quality,
+       r.tcn_source,
+       r.tcn_value,
+       title.value AS title,
+       uniform_title.value AS uniform_title,
+       author.value AS author,
+       publisher.value AS publisher,
+       SUBSTRING(pubdate.value FROM $$\d+$$) AS pubdate,
+       series_title.value AS series_title,
+       series_statement.value AS series_statement,
+       summary.value AS summary,
+       ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) AS isbn,
+       ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) AS issn,
+       ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '650' AND subfield = 'a' AND record = r.id)) AS topic_subject,
+       ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '651' AND subfield = 'a' AND record = r.id)) AS geographic_subject,
+       ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '655' AND subfield = 'a' AND record = r.id)) AS genre,
+       ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '600' AND subfield = 'a' AND record = r.id)) AS name_subject,
+       ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '610' AND subfield = 'a' AND record = r.id)) AS corporate_subject,
+       ARRAY((SELECT value FROM metabib.full_rec WHERE tag = '856' AND subfield IN ('3','y','u') AND record = r.id ORDER BY CASE WHEN subfield IN ('3','y') THEN 0 ELSE 1 END)) AS external_uri
+  FROM biblio.record_entry r
+       JOIN metabib.metarecord_source_map s ON (s.source = r.id)
+       LEFT JOIN metabib.full_rec uniform_title ON (r.id = uniform_title.record AND uniform_title.tag = '240' AND uniform_title.subfield = 'a')
+       LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
+       LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag = '100' AND author.subfield = 'a')
+       LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND publisher.tag = '260' AND publisher.subfield = 'b')
+       LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND pubdate.tag = '260' AND pubdate.subfield = 'c')
+       LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
+       LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
+       LEFT JOIN metabib.full_rec series_title ON (r.id = series_title.record AND series_title.tag IN ('830','440') AND series_title.subfield = 'a')
+       LEFT JOIN metabib.full_rec series_statement ON (r.id = series_statement.record AND series_statement.tag = '490' AND series_statement.subfield = 'a')
+       LEFT JOIN metabib.full_rec summary ON (r.id = summary.record AND summary.tag = '520' AND summary.subfield = 'a')
+  GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14;
+
+CREATE OR REPLACE VIEW reporter.old_super_simple_record AS
+SELECT  r.id,
+    r.fingerprint,
+    r.quality,
+    r.tcn_source,
+    r.tcn_value,
+    FIRST(title.value) AS title,
+    FIRST(author.value) AS author,
+    STRING_AGG(DISTINCT publisher.value, ', ') AS publisher,
+    STRING_AGG(DISTINCT SUBSTRING(pubdate.value FROM $$\d+$$), ', ') AS pubdate,
+    CASE WHEN ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) = '{NULL}'
+        THEN NULL
+        ELSE ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') )
+    END AS isbn,
+    CASE WHEN ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) = '{NULL}'
+        THEN NULL
+        ELSE ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') )
+    END AS issn
+  FROM  biblio.record_entry r
+    LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
+    LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag IN ('100','110','111') AND author.subfield = 'a')
+    LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND (publisher.tag = '260' OR (publisher.tag = '264' AND publisher.ind2 = '1')) AND publisher.subfield = 'b')
+    LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND (pubdate.tag = '260' OR (pubdate.tag = '264' AND pubdate.ind2 = '1')) AND pubdate.subfield = 'c')
+    LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
+    LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
+  GROUP BY 1,2,3,4,5;
+
+SELECT evergreen.upgrade_deps_block_check('0856', :eg_version);
+
+CREATE OR REPLACE FUNCTION metabib.staged_browse(
+    query                   TEXT,
+    fields                  INT[],
+    context_org             INT,
+    context_locations       INT[],
+    staff                   BOOL,
+    browse_superpage_size   INT,
+    count_up_from_zero      BOOL,   -- if false, count down from -1
+    result_limit            INT,
+    next_pivot_pos          INT
+) RETURNS SETOF metabib.flat_browse_entry_appearance AS $p$
+DECLARE
+    curs                    REFCURSOR;
+    rec                     RECORD;
+    qpfts_query             TEXT;
+    aqpfts_query            TEXT;
+    afields                 INT[];
+    bfields                 INT[];
+    result_row              metabib.flat_browse_entry_appearance%ROWTYPE;
+    results_skipped         INT := 0;
+    row_counter             INT := 0;
+    row_number              INT;
+    slice_start             INT;
+    slice_end               INT;
+    full_end                INT;
+    all_records             BIGINT[];
+    all_brecords             BIGINT[];
+    all_arecords            BIGINT[];
+    superpage_of_records    BIGINT[];
+    superpage_size          INT;
+BEGIN
+    IF count_up_from_zero THEN
+        row_number := 0;
+    ELSE
+        row_number := -1;
+    END IF;
+
+    OPEN curs FOR EXECUTE query;
+
+    LOOP
+        FETCH curs INTO rec;
+        IF NOT FOUND THEN
+            IF result_row.pivot_point IS NOT NULL THEN
+                RETURN NEXT result_row;
+            END IF;
+            RETURN;
+        END IF;
+
+
+        -- Gather aggregate data based on the MBE row we're looking at now, authority axis
+        SELECT INTO all_arecords, result_row.sees, afields
+                ARRAY_AGG(DISTINCT abl.bib), -- bibs to check for visibility
+                STRING_AGG(DISTINCT aal.source::TEXT, $$,$$), -- authority record ids
+                ARRAY_AGG(DISTINCT map.metabib_field) -- authority-tag-linked CMF rows
+
+          FROM  metabib.browse_entry_simple_heading_map mbeshm
+                JOIN authority.simple_heading ash ON ( mbeshm.simple_heading = ash.id )
+                JOIN authority.authority_linking aal ON ( ash.record = aal.source )
+                JOIN authority.bib_linking abl ON ( aal.target = abl.authority )
+                JOIN authority.control_set_auth_field_metabib_field_map_refs map ON (
+                    ash.atag = map.authority_field
+                    AND map.metabib_field = ANY(fields)
+                )
+          WHERE mbeshm.entry = rec.id;
+
+
+        -- Gather aggregate data based on the MBE row we're looking at now, bib axis
+        SELECT INTO all_brecords, result_row.authorities, bfields
+                ARRAY_AGG(DISTINCT source),
+                STRING_AGG(DISTINCT authority::TEXT, $$,$$),
+                ARRAY_AGG(DISTINCT def)
+          FROM  metabib.browse_entry_def_map
+          WHERE entry = rec.id
+                AND def = ANY(fields);
+
+        SELECT INTO result_row.fields STRING_AGG(DISTINCT x::TEXT, $$,$$) FROM UNNEST(afields || bfields) x;
+
+        result_row.sources := 0;
+        result_row.asources := 0;
+
+        -- Bib-linked vis checking
+        IF ARRAY_UPPER(all_brecords,1) IS NOT NULL THEN
+
+            full_end := ARRAY_LENGTH(all_brecords, 1);
+            superpage_size := COALESCE(browse_superpage_size, full_end);
+            slice_start := 1;
+            slice_end := superpage_size;
+
+            WHILE result_row.sources = 0 AND slice_start <= full_end LOOP
+                superpage_of_records := all_brecords[slice_start:slice_end];
+                qpfts_query :=
+                    'SELECT NULL::BIGINT AS id, ARRAY[r] AS records, ' ||
+                    '1::INT AS rel FROM (SELECT UNNEST(' ||
+                    quote_literal(superpage_of_records) || '::BIGINT[]) AS r) rr';
+
+                -- We use search.query_parser_fts() for visibility testing.
+                -- We're calling it once per browse-superpage worth of records
+                -- out of the set of records related to a given mbe, until we've
+                -- either exhausted that set of records or found at least 1
+                -- visible record.
+
+                SELECT INTO result_row.sources visible
+                    FROM search.query_parser_fts(
+                        context_org, NULL, qpfts_query, NULL,
+                        context_locations, 0, NULL, NULL, FALSE, staff, FALSE
+                    ) qpfts
+                    WHERE qpfts.rel IS NULL;
+
+                slice_start := slice_start + superpage_size;
+                slice_end := slice_end + superpage_size;
+            END LOOP;
+
+            -- Accurate?  Well, probably.
+            result_row.accurate := browse_superpage_size IS NULL OR
+                browse_superpage_size >= full_end;
+
+        END IF;
+
+        -- Authority-linked vis checking
+        IF ARRAY_UPPER(all_arecords,1) IS NOT NULL THEN
+
+            full_end := ARRAY_LENGTH(all_arecords, 1);
+            superpage_size := COALESCE(browse_superpage_size, full_end);
+            slice_start := 1;
+            slice_end := superpage_size;
+
+            WHILE result_row.asources = 0 AND slice_start <= full_end LOOP
+                superpage_of_records := all_arecords[slice_start:slice_end];
+                qpfts_query :=
+                    'SELECT NULL::BIGINT AS id, ARRAY[r] AS records, ' ||
+                    '1::INT AS rel FROM (SELECT UNNEST(' ||
+                    quote_literal(superpage_of_records) || '::BIGINT[]) AS r) rr';
+
+                -- We use search.query_parser_fts() for visibility testing.
+                -- We're calling it once per browse-superpage worth of records
+                -- out of the set of records related to a given mbe, via
+                -- authority until we've either exhausted that set of records
+                -- or found at least 1 visible record.
+
+                SELECT INTO result_row.asources visible
+                    FROM search.query_parser_fts(
+                        context_org, NULL, qpfts_query, NULL,
+                        context_locations, 0, NULL, NULL, FALSE, staff, FALSE
+                    ) qpfts
+                    WHERE qpfts.rel IS NULL;
+
+                slice_start := slice_start + superpage_size;
+                slice_end := slice_end + superpage_size;
+            END LOOP;
+
+
+            -- Accurate?  Well, probably.
+            result_row.aaccurate := browse_superpage_size IS NULL OR
+                browse_superpage_size >= full_end;
+
+        END IF;
+
+        IF result_row.sources > 0 OR result_row.asources > 0 THEN
+
+            -- The function that calls this function needs row_number in order
+            -- to correctly order results from two different runs of this
+            -- functions.
+            result_row.row_number := row_number;
+
+            -- Now, if row_counter is still less than limit, return a row.  If
+            -- not, but it is less than next_pivot_pos, continue on without
+            -- returning actual result rows until we find
+            -- that next pivot, and return it.
+
+            IF row_counter < result_limit THEN
+                result_row.browse_entry := rec.id;
+                result_row.value := rec.value;
+
+                RETURN NEXT result_row;
+            ELSE
+                result_row.browse_entry := NULL;
+                result_row.authorities := NULL;
+                result_row.fields := NULL;
+                result_row.value := NULL;
+                result_row.sources := NULL;
+                result_row.sees := NULL;
+                result_row.accurate := NULL;
+                result_row.aaccurate := NULL;
+                result_row.pivot_point := rec.id;
+
+                IF row_counter >= next_pivot_pos THEN
+                    RETURN NEXT result_row;
+                    RETURN;
+                END IF;
+            END IF;
+
+            IF count_up_from_zero THEN
+                row_number := row_number + 1;
+            ELSE
+                row_number := row_number - 1;
+            END IF;
+
+            -- row_counter is different from row_number.
+            -- It simply counts up from zero so that we know when
+            -- we've reached our limit.
+            row_counter := row_counter + 1;
+        END IF;
+    END LOOP;
+END;
+$p$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0857', :eg_version);
+
+INSERT INTO config.global_flag (name, enabled, label)
+VALUES (
+    'opac.located_uri.act_as_copy',
+    FALSE,
+    oils_i18n_gettext(
+        'opac.located_uri.act_as_copy',
+        'When enabled, Located URIs will provide visiblity behavior identical to copies.',
+        'cgf',
+        'label'
+    )
+);
+
+CREATE OR REPLACE FUNCTION evergreen.located_uris (
+    bibid BIGINT,
+    ouid INT,
+    pref_lib INT DEFAULT NULL
+) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
+    WITH all_orgs AS (SELECT COALESCE( enabled, FALSE ) AS flag FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy')
+    SELECT DISTINCT ON (id) * FROM (
+    SELECT acn.id, COALESCE(aou.name,aoud.name), acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
+      FROM asset.call_number acn
+           INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
+           INNER JOIN asset.uri auri ON auri.id = auricnm.uri
+           LEFT JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
+           LEFT JOIN actor.org_unit_descendants( COALESCE($3, $2) ) aoud ON (acn.owning_lib = aoud.id),
+           all_orgs
+      WHERE acn.record = $1
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE
+          AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL)
+    UNION
+    SELECT acn.id, COALESCE(aou.name,aoud.name) AS name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
+      FROM asset.call_number acn
+           INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
+           INNER JOIN asset.uri auri ON auri.id = auricnm.uri
+           LEFT JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
+           LEFT JOIN actor.org_unit_descendants( $2 ) aoud ON (acn.owning_lib = aoud.id),
+           all_orgs
+      WHERE acn.record = $1
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE
+          AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL))x
+    ORDER BY id, pref_ou DESC;
+$$
+LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION search.query_parser_fts (
+
+    param_search_ou INT,
+    param_depth     INT,
+    param_query     TEXT,
+    param_statuses  INT[],
+    param_locations INT[],
+    param_offset    INT,
+    param_check     INT,
+    param_limit     INT,
+    metarecord      BOOL,
+    staff           BOOL,
+    deleted_search  BOOL,
+    param_pref_ou   INT DEFAULT NULL
+) RETURNS SETOF search.search_result AS $func$
+DECLARE
+
+    current_res         search.search_result%ROWTYPE;
+    search_org_list     INT[];
+    luri_org_list       INT[];
+    tmp_int_list        INT[];
+
+    check_limit         INT;
+    core_limit          INT;
+    core_offset         INT;
+    tmp_int             INT;
+
+    core_result         RECORD;
+    core_cursor         REFCURSOR;
+    core_rel_query      TEXT;
+
+    total_count         INT := 0;
+    check_count         INT := 0;
+    deleted_count       INT := 0;
+    visible_count       INT := 0;
+    excluded_count      INT := 0;
+
+    luri_as_copy        BOOL;
+BEGIN
+
+    check_limit := COALESCE( param_check, 1000 );
+    core_limit  := COALESCE( param_limit, 25000 );
+    core_offset := COALESCE( param_offset, 0 );
+
+    SELECT COALESCE( enabled, FALSE ) INTO luri_as_copy FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy';
+
+    -- core_skip_chk := COALESCE( param_skip_chk, 1 );
+
+    IF param_search_ou > 0 THEN
+        IF param_depth IS NOT NULL THEN
+            SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
+        ELSE
+            SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
+        END IF;
+
+        IF luri_as_copy THEN
+            SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_full_path( param_search_ou );
+        ELSE
+            SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
+        END IF;
+
+    ELSIF param_search_ou < 0 THEN
+        SELECT ARRAY_AGG(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
+
+        FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
+
+            IF luri_as_copy THEN
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( tmp_int );
+            ELSE
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
+            END IF;
+
+            luri_org_list := luri_org_list || tmp_int_list;
+        END LOOP;
+
+        SELECT ARRAY_AGG(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
+
+    ELSIF param_search_ou = 0 THEN
+        -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
+    END IF;
+
+    IF param_pref_ou IS NOT NULL THEN
+            IF luri_as_copy THEN
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( param_pref_ou );
+            ELSE
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( param_pref_ou );
+            END IF;
+
+        luri_org_list := luri_org_list || tmp_int_list;
+    END IF;
+
+    OPEN core_cursor FOR EXECUTE param_query;
+
+    LOOP
+
+        FETCH core_cursor INTO core_result;
+        EXIT WHEN NOT FOUND;
+        EXIT WHEN total_count >= core_limit;
+
+        total_count := total_count + 1;
+
+        CONTINUE WHEN total_count NOT BETWEEN  core_offset + 1 AND check_limit + core_offset;
+
+        check_count := check_count + 1;
+
+        IF NOT deleted_search THEN
+
+            PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+            IF NOT FOUND THEN
+                -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
+                deleted_count := deleted_count + 1;
+                CONTINUE;
+            END IF;
+
+            PERFORM 1
+              FROM  biblio.record_entry b
+                    JOIN config.bib_source s ON (b.source = s.id)
+              WHERE s.transcendant
+                    AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+
+            IF FOUND THEN
+                -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
+                visible_count := visible_count + 1;
+
+                current_res.id = core_result.id;
+                current_res.rel = core_result.rel;
+
+                tmp_int := 1;
+                IF metarecord THEN
+                    SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+                END IF;
+
+                IF tmp_int = 1 THEN
+                    current_res.record = core_result.records[1];
+                ELSE
+                    current_res.record = NULL;
+                END IF;
+
+                RETURN NEXT current_res;
+
+                CONTINUE;
+            END IF;
+
+            PERFORM 1
+              FROM  asset.call_number cn
+                    JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
+                    JOIN asset.uri uri ON (map.uri = uri.id)
+              WHERE NOT cn.deleted
+                    AND cn.label = '##URI##'
+                    AND uri.active
+                    AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
+                    AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                    AND cn.owning_lib IN ( SELECT * FROM unnest( luri_org_list ) )
+              LIMIT 1;
+
+            IF FOUND THEN
+                -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
+                visible_count := visible_count + 1;
+
+                current_res.id = core_result.id;
+                current_res.rel = core_result.rel;
+
+                tmp_int := 1;
+                IF metarecord THEN
+                    SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+                END IF;
+
+                IF tmp_int = 1 THEN
+                    current_res.record = core_result.records[1];
+                ELSE
+                    current_res.record = NULL;
+                END IF;
+
+                RETURN NEXT current_res;
+
+                CONTINUE;
+            END IF;
+
+            IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+                    -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            END IF;
+
+            IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+                        -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            END IF;
+
+            IF staff IS NULL OR NOT staff THEN
+
+                PERFORM 1
+                  FROM  asset.opac_visible_copies
+                  WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy)
+                      WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+
+                        -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            ELSE
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+
+                        PERFORM 1
+                          FROM  asset.call_number cn
+                                JOIN asset.copy cp ON (cp.call_number = cn.id)
+                          WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                                AND NOT cp.deleted
+                          LIMIT 1;
+
+                        IF FOUND THEN
+                            -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                            excluded_count := excluded_count + 1;
+                            CONTINUE;
+                        END IF;
+                    END IF;
+
+                END IF;
+
+            END IF;
+
+        END IF;
+
+        visible_count := visible_count + 1;
+
+        current_res.id = core_result.id;
+        current_res.rel = core_result.rel;
+
+        tmp_int := 1;
+        IF metarecord THEN
+            SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+        END IF;
+
+        IF tmp_int = 1 THEN
+            current_res.record = core_result.records[1];
+        ELSE
+            current_res.record = NULL;
+        END IF;
+
+        RETURN NEXT current_res;
+
+        IF visible_count % 1000 = 0 THEN
+            -- RAISE NOTICE ' % visible so far ... ', visible_count;
+        END IF;
+
+    END LOOP;
+
+    current_res.id = NULL;
+    current_res.rel = NULL;
+    current_res.record = NULL;
+    current_res.total = total_count;
+    current_res.checked = check_count;
+    current_res.deleted = deleted_count;
+    current_res.visible = visible_count;
+    current_res.excluded = excluded_count;
+
+    CLOSE core_cursor;
+
+    RETURN NEXT current_res;
+
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION unapi.holdings_xml (
+    bid BIGINT,
+    ouid INT,
+    org TEXT,
+    depth INT DEFAULT NULL,
+    includes TEXT[] DEFAULT NULL::TEXT[],
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+)
+RETURNS XML AS $F$
+     SELECT  XMLELEMENT(
+                 name holdings,
+                 XMLATTRIBUTES(
+                    CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id,
+                    (SELECT record_has_holdable_copy FROM asset.record_has_holdable_copy($1)) AS has_holdable
+                 ),
+                 XMLELEMENT(
+                     name counts,
+                     (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.opac_ou_record_copy_count($2,  $1)
+                                     UNION
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.staff_ou_record_copy_count($2, $1)
+                                     UNION
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.opac_ou_record_copy_count($9,  $1)
+                                     ORDER BY 1
+                     )x)
+                 ),
+                 CASE
+                     WHEN ('bmp' = ANY ($5)) THEN
+                        XMLELEMENT(
+                            name monograph_parts,
+                            (SELECT XMLAGG(bmp) FROM (
+                                SELECT  unapi.bmp( id, 'xml', 'monograph_part', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7, FALSE)
+                                  FROM  biblio.monograph_part
+                                  WHERE record = $1
+                            )x)
+                        )
+                     ELSE NULL
+                 END,
+                 XMLELEMENT(
+                     name volumes,
+                     (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
+                        -- Physical copies
+                        SELECT  unapi.acn(y.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), y.rank, name, label_sortkey
+                        FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9, $5) AS y
+                        UNION ALL
+                        -- Located URIs
+                        SELECT unapi.acn(uris.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), uris.rank, name, label_sortkey
+                        FROM evergreen.located_uris($1, $2, $9) AS uris
+                     )x)
+                 ),
+                 CASE WHEN ('ssub' = ANY ($5)) THEN
+                     XMLELEMENT(
+                         name subscriptions,
+                         (SELECT XMLAGG(ssub) FROM (
+                            SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
+                              FROM  serial.subscription
+                              WHERE record_entry = $1
+                        )x)
+                     )
+                 ELSE NULL END,
+                 CASE WHEN ('acp' = ANY ($5)) THEN
+                     XMLELEMENT(
+                         name foreign_copies,
+                         (SELECT XMLAGG(acp) FROM (
+                            SELECT  unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
+                              FROM  biblio.peer_bib_copy_map p
+                                    JOIN asset.copy c ON (p.target_copy = c.id)
+                              WHERE NOT c.deleted AND p.peer_record = $1
+                            LIMIT ($6 -> 'acp')::INT
+                            OFFSET ($7 -> 'acp')::INT
+                        )x)
+                     )
+                 ELSE NULL END
+             );
+$F$ LANGUAGE SQL STABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0858', :eg_version);
+
+-- Fix faulty seed data. Otherwise for ptype 'f' we have subfield 'e'
+-- overlapping subfield 'd'
+UPDATE config.marc21_physical_characteristic_subfield_map
+    SET start_pos = 5
+    WHERE ptype_key = 'f' AND subfield = 'e';
+
+-- Evergreen DB patch 0859.data.staff-initials-settings.sql
+--
+-- More granular configuration settings for requiring use of staff initials
+--
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0859', :eg_version);
+
+-- add new granular settings for requiring use of staff initials
+INSERT INTO config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'ui.staff.require_initials.patron_standing_penalty',
+        'gui',
+        oils_i18n_gettext(
+            'ui.staff.require_initials.patron_standing_penalty',
+            'Require staff initials for entry/edit of patron standing penalties and messages.',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'ui.staff.require_initials.patron_standing_penalty',
+            'Appends staff initials and edit date into patron standing penalties and messages.',
+            'coust',
+            'description'
+        ),
+        'bool'
+    ), (
+        'ui.staff.require_initials.patron_info_notes',
+        'gui',
+        oils_i18n_gettext(
+            'ui.staff.require_initials.patron_info_notes',
+            'Require staff initials for entry/edit of patron notes.',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'ui.staff.require_initials.patron_info_notes',
+            'Appends staff initials and edit date into patron note content.',
+            'coust',
+            'description'
+        ),
+        'bool'
+    ), (
+        'ui.staff.require_initials.copy_notes',
+        'gui',
+        oils_i18n_gettext(
+            'ui.staff.require_initials.copy_notes',
+            'Require staff initials for entry/edit of copy notes.',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'ui.staff.require_initials.copy_notes',
+            'Appends staff initials and edit date into copy note content..',
+            'coust',
+            'description'
+        ),
+        'bool'
+    );
+
+-- Update any existing setting so that the original set value is now passed to
+-- one of the newer settings.
+
+UPDATE actor.org_unit_setting
+SET name = 'ui.staff.require_initials.patron_standing_penalty'
+WHERE name = 'ui.staff.require_initials';
+
+-- Add similar values for new settings as old ones to preserve existing configured
+-- functionality.
+
+INSERT INTO actor.org_unit_setting (org_unit, name, value)
+SELECT org_unit, 'ui.staff.require_initials.patron_info_notes', value
+FROM actor.org_unit_setting
+WHERE name = 'ui.staff.require_initials.patron_standing_penalty';
+
+INSERT INTO actor.org_unit_setting (org_unit, name, value)
+SELECT org_unit, 'ui.staff.require_initials.copy_notes', value
+FROM actor.org_unit_setting
+WHERE name = 'ui.staff.require_initials.patron_standing_penalty';
+
+-- Update setting logs so that the original setting name's history is now transferred
+-- over to one of the newer settings.
+
+UPDATE config.org_unit_setting_type_log
+SET field_name = 'ui.staff.require_initials.patron_standing_penalty'
+WHERE field_name = 'ui.staff.require_initials';
+
+-- Remove the old setting entirely
+
+DELETE FROM config.org_unit_setting_type WHERE name = 'ui.staff.require_initials';
+
+-- oh, the irony
+SELECT evergreen.upgrade_deps_block_check('0860', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.array_overlap_check (/* field */) RETURNS TRIGGER AS $$
+DECLARE
+    fld     TEXT;
+    cnt     INT;
+BEGIN
+    fld := TG_ARGV[0];
+    EXECUTE 'SELECT COUNT(*) FROM '|| TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME ||' WHERE '|| fld ||' && ($1).'|| fld INTO cnt USING NEW;
+    IF cnt > 0 THEN
+        RAISE EXCEPTION 'Cannot insert duplicate array into field % of table %', fld, TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME;
+    END IF;
+    RETURN NEW;
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecates ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
+    SELECT  DISTINCT l.version
+      FROM  config.upgrade_log l
+            JOIN config.db_patch_dependencies d ON (l.version = ANY(d.deprecates))
+      WHERE d.db_patch = $1
+$$ LANGUAGE SQL;
+
+-- List applied db patches that are superseded by (and block the application of) my_db_patch
+CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_supersedes ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
+    SELECT  DISTINCT l.version
+      FROM  config.upgrade_log l
+            JOIN config.db_patch_dependencies d ON (l.version = ANY(d.supersedes))
+      WHERE d.db_patch = $1
+$$ LANGUAGE SQL;
+
+
+CREATE OR REPLACE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
+DECLARE 
+    deprecates TEXT;
+    supersedes TEXT;
+BEGIN
+    IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
+        SELECT  STRING_AGG(patch, ', ') INTO deprecates FROM evergreen.upgrade_list_applied_deprecates(my_db_patch);
+        SELECT  STRING_AGG(patch, ', ') INTO supersedes FROM evergreen.upgrade_list_applied_supersedes(my_db_patch);
+        RAISE EXCEPTION '
+Upgrade script % can not be applied:
+  applied deprecated scripts %
+  applied superseded scripts %
+  deprecated by %
+  superseded by %',
+            my_db_patch,
+            (SELECT ARRAY_AGG(patch) FROM evergreen.upgrade_list_applied_deprecates(my_db_patch)),
+            (SELECT ARRAY_AGG(patch) FROM evergreen.upgrade_list_applied_supersedes(my_db_patch)),
+            evergreen.upgrade_list_applied_deprecated(my_db_patch),
+            evergreen.upgrade_list_applied_superseded(my_db_patch);
+    END IF;
+
+    INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
+    RETURN TRUE;
+END;
+$$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0861', :eg_version);
+
+CREATE INDEX authority_record_entry_create_date_idx ON authority.record_entry ( create_date );
+CREATE INDEX authority_record_entry_edit_date_idx ON authority.record_entry ( edit_date );
+
+-- this file is a duplicate of 0851, moved up for better backport clarity
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0862', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.maintain_901 () RETURNS TRIGGER AS $func$
+use strict;
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+use MARC::Charset;
+use Encode;
+use Unicode::Normalize;
+
+MARC::Charset->assume_unicode(1);
+
+my $schema = $_TD->{table_schema};
+my $marc = MARC::Record->new_from_xml($_TD->{new}{marc});
+
+my @old901s = $marc->field('901');
+$marc->delete_fields(@old901s);
+
+if ($schema eq 'biblio') {
+    my $tcn_value = $_TD->{new}{tcn_value};
+
+    # Set TCN value to record ID?
+    my $id_as_tcn = spi_exec_query("
+        SELECT enabled
+        FROM config.global_flag
+        WHERE name = 'cat.bib.use_id_for_tcn'
+    ");
+    if (($id_as_tcn->{processed}) && $id_as_tcn->{rows}[0]->{enabled} eq 't') {
+        $tcn_value = $_TD->{new}{id}; 
+        $_TD->{new}{tcn_value} = $tcn_value;
+    }
+
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "a" => $tcn_value,
+        "b" => $_TD->{new}{tcn_source},
+        "c" => $_TD->{new}{id},
+        "t" => $schema
+    );
+
+    if ($_TD->{new}{owner}) {
+        $new_901->add_subfields("o" => $_TD->{new}{owner});
+    }
+
+    if ($_TD->{new}{share_depth}) {
+        $new_901->add_subfields("d" => $_TD->{new}{share_depth});
+    }
+
+    $marc->append_fields($new_901);
+} elsif ($schema eq 'authority') {
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "c" => $_TD->{new}{id},
+        "t" => $schema,
+    );
+    $marc->append_fields($new_901);
+} elsif ($schema eq 'serial') {
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "c" => $_TD->{new}{id},
+        "t" => $schema,
+        "o" => $_TD->{new}{owning_lib},
+    );
+
+    if ($_TD->{new}{record}) {
+        $new_901->add_subfields("r" => $_TD->{new}{record});
+    }
+
+    $marc->append_fields($new_901);
+} else {
+    my $new_901 = MARC::Field->new("901", " ", " ",
+        "c" => $_TD->{new}{id},
+        "t" => $schema,
+    );
+    $marc->append_fields($new_901);
+}
+
+my $xml = $marc->as_xml_record();
+$xml =~ s/\n//sgo;
+$xml =~ s/^<\?xml.+\?\s*>//go;
+$xml =~ s/>\s+</></go;
+$xml =~ s/\p{Cc}//go;
+
+# Embed a version of OpenILS::Application::AppUtils->entityize()
+# to avoid having to set PERL5LIB for PostgreSQL as well
+
+$xml = NFC($xml);
+
+# Convert raw ampersands to entities
+$xml =~ s/&(?!\S+;)/&amp;/gso;
+
+# Convert Unicode characters to entities
+$xml =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
+
+$xml =~ s/[\x00-\x1f]//go;
+$_TD->{new}{marc} = $xml;
+
+return "MODIFY";
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION maintain_control_numbers() RETURNS TRIGGER AS $func$
+use strict;
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+use MARC::Charset;
+use Encode;
+use Unicode::Normalize;
+
+MARC::Charset->assume_unicode(1);
+
+my $record = MARC::Record->new_from_xml($_TD->{new}{marc});
+my $schema = $_TD->{table_schema};
+my $rec_id = $_TD->{new}{id};
+
+# Short-circuit if maintaining control numbers per MARC21 spec is not enabled
+my $enable = spi_exec_query("SELECT enabled FROM config.global_flag WHERE name = 'cat.maintain_control_numbers'");
+if (!($enable->{processed}) or $enable->{rows}[0]->{enabled} eq 'f') {
+    return;
+}
+
+# Get the control number identifier from an OU setting based on $_TD->{new}{owner}
+my $ou_cni = 'EVRGRN';
+
+my $owner;
+if ($schema eq 'serial') {
+    $owner = $_TD->{new}{owning_lib};
+} else {
+    # are.owner and bre.owner can be null, so fall back to the consortial setting
+    $owner = $_TD->{new}{owner} || 1;
+}
+
+my $ous_rv = spi_exec_query("SELECT value FROM actor.org_unit_ancestor_setting('cat.marc_control_number_identifier', $owner)");
+if ($ous_rv->{processed}) {
+    $ou_cni = $ous_rv->{rows}[0]->{value};
+    $ou_cni =~ s/"//g; # Stupid VIM syntax highlighting"
+} else {
+    # Fall back to the shortname of the OU if there was no OU setting
+    $ous_rv = spi_exec_query("SELECT shortname FROM actor.org_unit WHERE id = $owner");
+    if ($ous_rv->{processed}) {
+        $ou_cni = $ous_rv->{rows}[0]->{shortname};
+    }
+}
+
+my ($create, $munge) = (0, 0);
+
+my @scns = $record->field('035');
+
+foreach my $id_field ('001', '003') {
+    my $spec_value;
+    my @controls = $record->field($id_field);
+
+    if ($id_field eq '001') {
+        $spec_value = $rec_id;
+    } else {
+        $spec_value = $ou_cni;
+    }
+
+    # Create the 001/003 if none exist
+    if (scalar(@controls) == 1) {
+        # Only one field; check to see if we need to munge it
+        unless (grep $_->data() eq $spec_value, @controls) {
+            $munge = 1;
+        }
+    } else {
+        # Delete the other fields, as with more than 1 001/003 we do not know which 003/001 to match
+        foreach my $control (@controls) {
+            $record->delete_field($control);
+        }
+        $record->insert_fields_ordered(MARC::Field->new($id_field, $spec_value));
+        $create = 1;
+    }
+}
+
+my $cn = $record->field('001')->data();
+# Special handling of OCLC numbers, often found in records that lack 003
+if ($cn =~ /^o(c[nm]|n)\d/) {
+    $cn =~ s/^o(c[nm]|n)0*(\d+)/$2/;
+    $record->field('003')->data('OCoLC');
+    $create = 0;
+}
+
+# Now, if we need to munge the 001, we will first push the existing 001/003
+# into the 035; but if the record did not have one (and one only) 001 and 003
+# to begin with, skip this process
+if ($munge and not $create) {
+
+    my $scn = "(" . $record->field('003')->data() . ")" . $cn;
+
+    # Do not create duplicate 035 fields
+    unless (grep $_->subfield('a') eq $scn, @scns) {
+        $record->insert_fields_ordered(MARC::Field->new('035', '', '', 'a' => $scn));
+    }
+}
+
+# Set the 001/003 and update the MARC
+if ($create or $munge) {
+    $record->field('001')->data($rec_id);
+    $record->field('003')->data($ou_cni);
+
+    my $xml = $record->as_xml_record();
+    $xml =~ s/\n//sgo;
+    $xml =~ s/^<\?xml.+\?\s*>//go;
+    $xml =~ s/>\s+</></go;
+    $xml =~ s/\p{Cc}//go;
+
+    # Embed a version of OpenILS::Application::AppUtils->entityize()
+    # to avoid having to set PERL5LIB for PostgreSQL as well
+
+    $xml = NFC($xml);
+
+    # Convert raw ampersands to entities
+    $xml =~ s/&(?!\S+;)/&amp;/gso;
+
+    # Convert Unicode characters to entities
+    $xml =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
+
+    $xml =~ s/[\x00-\x1f]//go;
+    $_TD->{new}{marc} = $xml;
+
+    return "MODIFY";
+}
+
+return;
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION public.naco_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
+
+    use strict;
+    use Unicode::Normalize;
+    use Encode;
+
+    my $str = shift;
+    my $sf = shift;
+
+    # Apply NACO normalization to input string; based on
+    # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
+    #
+    # Note that unlike a strict reading of the NACO normalization rules,
+    # output is returned as lowercase instead of uppercase for compatibility
+    # with previous versions of the Evergreen naco_normalize routine.
+
+    # Convert to upper-case first; even though final output will be lowercase, doing this will
+    # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
+    # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
+    $str = uc $str;
+
+    # remove non-filing strings
+    $str =~ s/\x{0098}.*?\x{009C}//g;
+
+    $str = NFKD($str);
+
+    # additional substitutions - 3.6.
+    $str =~ s/\x{00C6}/AE/g;
+    $str =~ s/\x{00DE}/TH/g;
+    $str =~ s/\x{0152}/OE/g;
+    $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}]['/DDOLl/d;
+
+    # transformations based on Unicode category codes
+    $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
+
+       if ($sf && $sf =~ /^a/o) {
+               my $commapos = index($str, ',');
+               if ($commapos > -1) {
+                       if ($commapos != length($str) - 1) {
+                $str =~ s/,/\x07/; # preserve first comma
+                       }
+               }
+       }
+
+    # since we've stripped out the control characters, we can now
+    # use a few as placeholders temporarily
+    $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
+    $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
+    $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
+
+    # decimal digits
+    $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
+
+    # intentionally skipping step 8 of the NACO algorithm; if the string
+    # gets normalized away, that's fine.
+
+    # leading and trailing spaces
+    $str =~ s/\s+/ /g;
+    $str =~ s/^\s+//;
+    $str =~ s/\s+$//g;
+
+    return lc $str;
+$func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
+
+-- Currently, the only difference from naco_normalize is that search_normalize
+-- turns apostrophes into spaces, while naco_normalize collapses them.
+CREATE OR REPLACE FUNCTION public.search_normalize( TEXT, TEXT ) RETURNS TEXT AS $func$
+
+    use strict;
+    use Unicode::Normalize;
+    use Encode;
+
+    my $str = shift;
+    my $sf = shift;
+
+    # Apply NACO normalization to input string; based on
+    # http://www.loc.gov/catdir/pcc/naco/SCA_PccNormalization_Final_revised.pdf
+    #
+    # Note that unlike a strict reading of the NACO normalization rules,
+    # output is returned as lowercase instead of uppercase for compatibility
+    # with previous versions of the Evergreen naco_normalize routine.
+
+    # Convert to upper-case first; even though final output will be lowercase, doing this will
+    # ensure that the German eszett (ß) and certain ligatures (ff, fi, ffl, etc.) will be handled correctly.
+    # If there are any bugs in Perl's implementation of upcasing, they will be passed through here.
+    $str = uc $str;
+
+    # remove non-filing strings
+    $str =~ s/\x{0098}.*?\x{009C}//g;
+
+    $str = NFKD($str);
+
+    # additional substitutions - 3.6.
+    $str =~ s/\x{00C6}/AE/g;
+    $str =~ s/\x{00DE}/TH/g;
+    $str =~ s/\x{0152}/OE/g;
+    $str =~ tr/\x{0110}\x{00D0}\x{00D8}\x{0141}\x{2113}\x{02BB}\x{02BC}][/DDOLl/d;
+
+    # transformations based on Unicode category codes
+    $str =~ s/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Lm}\p{Mc}\p{Me}\p{Mn}]//g;
+
+       if ($sf && $sf =~ /^a/o) {
+               my $commapos = index($str, ',');
+               if ($commapos > -1) {
+                       if ($commapos != length($str) - 1) {
+                $str =~ s/,/\x07/; # preserve first comma
+                       }
+               }
+       }
+
+    # since we've stripped out the control characters, we can now
+    # use a few as placeholders temporarily
+    $str =~ tr/+&@\x{266D}\x{266F}#/\x01\x02\x03\x04\x05\x06/;
+    $str =~ s/[\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\p{Sk}\p{Sm}\p{So}\p{Zl}\p{Zp}\p{Zs}]/ /g;
+    $str =~ tr/\x01\x02\x03\x04\x05\x06\x07/+&@\x{266D}\x{266F}#,/;
+
+    # decimal digits
+    $str =~ tr/\x{0660}-\x{0669}\x{06F0}-\x{06F9}\x{07C0}-\x{07C9}\x{0966}-\x{096F}\x{09E6}-\x{09EF}\x{0A66}-\x{0A6F}\x{0AE6}-\x{0AEF}\x{0B66}-\x{0B6F}\x{0BE6}-\x{0BEF}\x{0C66}-\x{0C6F}\x{0CE6}-\x{0CEF}\x{0D66}-\x{0D6F}\x{0E50}-\x{0E59}\x{0ED0}-\x{0ED9}\x{0F20}-\x{0F29}\x{1040}-\x{1049}\x{1090}-\x{1099}\x{17E0}-\x{17E9}\x{1810}-\x{1819}\x{1946}-\x{194F}\x{19D0}-\x{19D9}\x{1A80}-\x{1A89}\x{1A90}-\x{1A99}\x{1B50}-\x{1B59}\x{1BB0}-\x{1BB9}\x{1C40}-\x{1C49}\x{1C50}-\x{1C59}\x{A620}-\x{A629}\x{A8D0}-\x{A8D9}\x{A900}-\x{A909}\x{A9D0}-\x{A9D9}\x{AA50}-\x{AA59}\x{ABF0}-\x{ABF9}\x{FF10}-\x{FF19}/0-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-90-9/;
+
+    # intentionally skipping step 8 of the NACO algorithm; if the string
+    # gets normalized away, that's fine.
+
+    # leading and trailing spaces
+    $str =~ s/\s+/ /g;
+    $str =~ s/^\s+//;
+    $str =~ s/\s+$//g;
+
+    return lc $str;
+$func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0863', :eg_version);
+
+
+-- cheat sheet for enabling Stripe payments:
+--  'credit.payments.allow' must be true, and among other things it drives the
+--      opac to render a payment form at all
+--  NEW 'credit.processor.stripe.enabled' must be true  (kind of redundant but
+--      my fault for setting the precedent with c.p.{authorizenet|paypal|payflowpro}.enabled)
+--  'credit.default.processor' must be 'Stripe'
+--  NEW 'credit.processor.stripe.pubkey' must be set
+--  NEW 'credit.processor.stripe.secretkey' must be set
+
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+
+    ( 'credit.processor.stripe.enabled', 'credit',
+    oils_i18n_gettext('credit.processor.stripe.enabled',
+        'Enable Stripe payments',
+        'coust', 'label'),
+    oils_i18n_gettext('credit.processor.stripe.enabled',
+        'Enable Stripe payments',
+        'coust', 'description'),
+    'bool', null)
+
+,( 'credit.processor.stripe.pubkey', 'credit',
+    oils_i18n_gettext('credit.processor.stripe.pubkey',
+        'Stripe publishable key',
+        'coust', 'label'),
+    oils_i18n_gettext('credit.processor.stripe.pubkey',
+        'Stripe publishable key',
+        'coust', 'description'),
+    'string', null)
+
+,( 'credit.processor.stripe.secretkey', 'credit',
+    oils_i18n_gettext('credit.processor.stripe.secretkey',
+        'Stripe secret key',
+        'coust', 'label'),
+    oils_i18n_gettext('credit.processor.stripe.secretkey',
+        'Stripe secret key',
+        'coust', 'description'),
+    'string', null)
+;
+
+UPDATE config.org_unit_setting_type
+SET description = 'This might be "AuthorizeNet", "PayPal", "PayflowPro", or "Stripe".'
+WHERE name = 'credit.processor.default' AND description = 'This might be "AuthorizeNet", "PayPal", etc.'; -- don't clobber local edits or i18n
+
+SELECT evergreen.upgrade_deps_block_check('0864', :eg_version);
+
+CREATE EXTENSION IF NOT EXISTS intarray;
+
+-- while we have this opportunity, and before we start collecting 
+-- CCVM IDs (below) carve out a nice space for stock ccvm values
+UPDATE config.coded_value_map SET id = id + 10000 WHERE id > 556;
+SELECT SETVAL('config.coded_value_map_id_seq'::TEXT, 
+    (SELECT GREATEST(max(id), 10000) FROM config.coded_value_map));
+
+ALTER TABLE config.record_attr_definition ADD COLUMN multi BOOL NOT NULL DEFAULT TRUE, ADD COLUMN composite BOOL NOT NULL DEFAULT FALSE;
+
+UPDATE  config.record_attr_definition
+  SET   multi = FALSE
+  WHERE name IN ('bib_level','control_type','pubdate','cat_form','enc_level','item_type','titlesort','authorsort');
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+DECLARE
+    rowid   INT := 0;
+    _007    TEXT;
+    ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
+    psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
+    pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
+    retval  biblio.marc21_physical_characteristics%ROWTYPE;
+BEGIN
+
+    FOR _007 IN SELECT oils_xpath_string('//*', value) FROM UNNEST(oils_xpath('//*[@tag="007"]', marc)) x(value) LOOP
+        IF _007 IS NOT NULL AND _007 <> '' THEN
+            SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
+
+            IF ptype.ptype_key IS NOT NULL THEN
+                FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
+                    SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
+
+                    IF pval.id IS NOT NULL THEN
+                        rowid := rowid + 1;
+                        retval.id := rowid;
+                        retval.ptype := ptype.ptype_key;
+                        retval.subfield := psf.id;
+                        retval.value := pval.id;
+                        RETURN NEXT retval;
+                    END IF;
+
+                END LOOP;
+            END IF;
+        END IF;
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field_list( marc TEXT, ff TEXT ) RETURNS TEXT[] AS $func$
+DECLARE
+    rtype       TEXT;
+    ff_pos      RECORD;
+    tag_data    RECORD;
+    val         TEXT;
+    collection  TEXT[] := '{}'::TEXT[];
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
+        IF ff_pos.tag = 'ldr' THEN
+            val := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF val IS NOT NULL THEN
+                val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
+                collection := collection || val;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+                collection := collection || val;
+            END LOOP;
+        END IF;
+        val := REPEAT( ff_pos.default_val, ff_pos.length );
+        collection := collection || val;
+    END LOOP;
+
+    RETURN collection;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_fixed_field_list( rid BIGINT, ff TEXT ) RETURNS TEXT[] AS $func$
+    SELECT * FROM vandelay.marc21_extract_fixed_field_list( (SELECT marc FROM biblio.record_entry WHERE id = $1), $2 );
+$func$ LANGUAGE SQL;
+
+-- DECREMENTING serial starts at -1
+CREATE SEQUENCE metabib.uncontrolled_record_attr_value_id_seq INCREMENT BY -1;
+
+CREATE TABLE metabib.uncontrolled_record_attr_value (
+    id      BIGINT  PRIMARY KEY DEFAULT nextval('metabib.uncontrolled_record_attr_value_id_seq'),
+    attr    TEXT    NOT NULL REFERENCES config.record_attr_definition (name),
+    value   TEXT    NOT NULL
+);
+CREATE UNIQUE INDEX muv_once_idx ON metabib.uncontrolled_record_attr_value (attr,value);
+
+CREATE TABLE metabib.record_attr_vector_list (
+    source  BIGINT  PRIMARY KEY REFERENCES  biblio.record_entry (id),
+    vlist   INT[]   NOT NULL -- stores id from ccvm AND murav
+);
+CREATE INDEX mrca_vlist_idx ON metabib.record_attr_vector_list USING gin ( vlist gin__int_ops );
+
+CREATE TABLE metabib.record_sorter (
+    id      BIGSERIAL   PRIMARY KEY,
+    source  BIGINT      NOT NULL REFERENCES biblio.record_entry (id) ON DELETE CASCADE,
+    attr    TEXT        NOT NULL REFERENCES config.record_attr_definition (name) ON DELETE CASCADE,
+    value   TEXT        NOT NULL
+);
+CREATE INDEX metabib_sorter_source_idx ON metabib.record_sorter (source); -- we may not need one of this or the next ... stats will tell
+CREATE INDEX metabib_sorter_s_a_idx ON metabib.record_sorter (source, attr);
+CREATE INDEX metabib_sorter_a_v_idx ON metabib.record_sorter (attr, value);
+
+CREATE TEMP TABLE attr_set ON COMMIT DROP AS SELECT  DISTINCT id AS source, (each(attrs)).key,(each(attrs)).value FROM metabib.record_attr;
+DELETE FROM attr_set WHERE BTRIM(value) = '';
+
+-- Grab sort values for the new sorting mechanism
+INSERT INTO metabib.record_sorter (source,attr,value)
+    SELECT  a.source, a.key, a.value
+      FROM  attr_set a
+            JOIN config.record_attr_definition d ON (d.name = a.key AND d.sorter AND a.value IS NOT NULL);
+
+-- Rewrite uncontrolled SVF record attrs as the seeds of an intarray vector
+INSERT INTO metabib.uncontrolled_record_attr_value (attr,value)
+    SELECT  DISTINCT a.key, a.value
+      FROM  attr_set a
+            JOIN config.record_attr_definition d ON (d.name = a.key AND d.filter AND a.value IS NOT NULL)
+            LEFT JOIN config.coded_value_map m ON (m.ctype = a.key)
+      WHERE m.id IS NULL;
+
+-- Now construct the record-specific vector from the SVF data
+INSERT INTO metabib.record_attr_vector_list (source,vlist)
+    SELECT  a.id, ARRAY_AGG(COALESCE(u.id, c.id))
+      FROM  metabib.record_attr a
+            JOIN attr_set ON (a.id = attr_set.source)
+            LEFT JOIN metabib.uncontrolled_record_attr_value u ON (u.attr = attr_set.key AND u.value = attr_set.value)
+            LEFT JOIN config.coded_value_map c ON (c.ctype = attr_set.key AND c.code = attr_set.value)
+      WHERE COALESCE(u.id,c.id) IS NOT NULL
+      GROUP BY 1;
+
+DROP VIEW IF EXISTS reporter.classic_current_circ; 
+DROP VIEW metabib.rec_descriptor;
+DROP TABLE metabib.record_attr;
+
+CREATE TYPE metabib.record_attr_type AS (
+    id      BIGINT,
+    attrs   HSTORE
+);
+
+CREATE TABLE config.composite_attr_entry_definition(
+    coded_value INT  PRIMARY KEY NOT NULL REFERENCES config.coded_value_map (id) ON UPDATE CASCADE ON DELETE CASCADE,
+    definition  TEXT NOT NULL -- JSON
+);
+
+CREATE OR REPLACE VIEW metabib.record_attr_id_map AS
+    SELECT id, attr, value FROM metabib.uncontrolled_record_attr_value
+        UNION
+    SELECT  c.id, c.ctype AS attr, c.code AS value
+      FROM  config.coded_value_map c
+            JOIN config.record_attr_definition d ON (d.name = c.ctype AND NOT d.composite);
+
+CREATE VIEW metabib.composite_attr_id_map AS
+    SELECT  c.id, c.ctype AS attr, c.code AS value
+      FROM  config.coded_value_map c
+            JOIN config.record_attr_definition d ON (d.name = c.ctype AND d.composite);
+
+CREATE OR REPLACE VIEW metabib.full_attr_id_map AS
+    SELECT id, attr, value FROM metabib.record_attr_id_map
+        UNION
+    SELECT id, attr, value FROM metabib.composite_attr_id_map;
+
+
+-- Back-compat view ... we're moving to an INTARRAY world
+CREATE VIEW metabib.record_attr_flat AS
+    SELECT  v.source AS id,
+            m.attr,
+            m.value
+      FROM  metabib.full_attr_id_map m
+            JOIN  metabib.record_attr_vector_list v ON ( m.id = ANY( v.vlist ) );
+
+CREATE VIEW metabib.record_attr AS
+    SELECT id, HSTORE( ARRAY_AGG( attr ), ARRAY_AGG( value ) ) AS attrs FROM metabib.record_attr_flat GROUP BY 1;
+
+CREATE VIEW metabib.rec_descriptor AS
+    SELECT  id,
+            id AS record,
+            (populate_record(NULL::metabib.rec_desc_type, attrs)).*
+      FROM  metabib.record_attr;
+
+CREATE OR REPLACE FUNCTION metabib.compile_composite_attr_cache_init () RETURNS BOOL AS $f$
+    $_SHARED{metabib_compile_composite_attr_cache} = {}
+        if ! exists $_SHARED{metabib_compile_composite_attr_cache};
+    return exists $_SHARED{metabib_compile_composite_attr_cache};
+$f$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION metabib.compile_composite_attr_cache_disable () RETURNS BOOL AS $f$
+    delete $_SHARED{metabib_compile_composite_attr_cache};
+    return ! exists $_SHARED{metabib_compile_composite_attr_cache};
+$f$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION metabib.compile_composite_attr_cache_invalidate () RETURNS BOOL AS $f$
+    SELECT metabib.compile_composite_attr_cache_disable() AND metabib.compile_composite_attr_cache_init();
+$f$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION metabib.composite_attr_def_cache_inval_tgr () RETURNS TRIGGER AS $f$
+BEGIN
+    PERFORM metabib.compile_composite_attr_cache_invalidate();
+    RETURN NULL;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER ccraed_cache_inval_tgr AFTER INSERT OR UPDATE OR DELETE ON config.composite_attr_entry_definition FOR EACH STATEMENT EXECUTE PROCEDURE metabib.composite_attr_def_cache_inval_tgr();
+    
+CREATE OR REPLACE FUNCTION metabib.compile_composite_attr ( cattr_def TEXT ) RETURNS query_int AS $func$
+
+    use JSON::XS;
+
+    my $json = shift;
+    my $def = decode_json($json);
+
+    die("Composite attribute definition not supplied") unless $def;
+
+    my $_cache = (exists $_SHARED{metabib_compile_composite_attr_cache}) ? 1 : 0;
+
+    return $_SHARED{metabib_compile_composite_attr_cache}{$json}
+        if ($_cache && $_SHARED{metabib_compile_composite_attr_cache}{$json});
+
+    sub recurse {
+        my $d = shift;
+        my $j = '&';
+        my @list;
+
+        if (ref $d eq 'HASH') { # node or AND
+            if (exists $d->{_attr}) { # it is a node
+                my $plan = spi_prepare('SELECT * FROM metabib.full_attr_id_map WHERE attr = $1 AND value = $2', qw/TEXT TEXT/);
+                my $id = spi_exec_prepared(
+                    $plan, {limit => 1}, $d->{_attr}, $d->{_val}
+                )->{rows}[0]{id};
+                spi_freeplan($plan);
+                return $id;
+            } elsif (exists $d->{_not} && scalar(keys(%$d)) == 1) { # it is a NOT
+                return '!' . recurse($$d{_not});
+            } else { # an AND list
+                @list = map { recurse($$d{$_}) } sort keys %$d;
+            }
+        } elsif (ref $d eq 'ARRAY') {
+            $j = '|';
+            @list = map { recurse($_) } @$d;
+        }
+
+        @list = grep { defined && $_ ne '' } @list;
+
+        return '(' . join($j,@list) . ')' if @list;
+        return '';
+    }
+
+    my $val = recurse($def) || undef;
+    $_SHARED{metabib_compile_composite_attr_cache}{$json} = $val if $_cache;
+    return $val;
+
+$func$ IMMUTABLE LANGUAGE plperlu;
+
+CREATE OR REPLACE FUNCTION metabib.compile_composite_attr ( cattr_id INT ) RETURNS query_int AS $func$
+    SELECT metabib.compile_composite_attr(definition) FROM config.composite_attr_entry_definition WHERE coded_value = $1;
+$func$ STRICT IMMUTABLE LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION metabib.reingest_record_attributes (rid BIGINT, pattr_list TEXT[] DEFAULT NULL, prmarc TEXT DEFAULT NULL, rdeleted BOOL DEFAULT TRUE) RETURNS VOID AS $func$
+DECLARE
+    transformed_xml TEXT;
+    rmarc           TEXT := prmarc;
+    tmp_val         TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_vector     INT[] := '{}'::INT[];
+    attr_vector_tmp INT[];
+    attr_list       TEXT[] := pattr_list;
+    attr_value      TEXT[];
+    norm_attr_value TEXT[];
+    tmp_xml         XML;
+    attr_def        config.record_attr_definition%ROWTYPE;
+    ccvm_row        config.coded_value_map%ROWTYPE;
+BEGIN
+
+    IF attr_list IS NULL OR rdeleted THEN -- need to do the full dance on INSERT or undelete
+        SELECT ARRAY_AGG(name) INTO attr_list FROM config.record_attr_definition;
+    END IF;
+
+    IF rmarc IS NULL THEN
+        SELECT marc INTO rmarc FROM biblio.record_entry WHERE id = rid;
+    END IF;
+
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE NOT composite AND name = ANY( attr_list ) ORDER BY format LOOP
+
+        attr_value := '{}'::TEXT[];
+        norm_attr_value := '{}'::TEXT[];
+        attr_vector_tmp := '{}'::INT[];
+
+        SELECT * INTO ccvm_row FROM config.coded_value_map c WHERE c.ctype = attr_def.name LIMIT 1; 
+
+        -- tag+sf attrs only support SVF
+        IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+            SELECT  ARRAY[ARRAY_TO_STRING(ARRAY_AGG(value), COALESCE(attr_def.joiner,' '))] INTO attr_value
+              FROM  (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
+              WHERE record = rid
+                    AND tag LIKE attr_def.tag
+                    AND CASE
+                        WHEN attr_def.sf_list IS NOT NULL 
+                            THEN POSITION(subfield IN attr_def.sf_list) > 0
+                        ELSE TRUE
+                    END
+              GROUP BY tag
+              ORDER BY tag
+              LIMIT 1;
+
+        ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+            attr_value := vandelay.marc21_extract_fixed_field_list(rmarc, attr_def.fixed_field);
+
+            IF NOT attr_def.multi THEN
+                attr_value := ARRAY[attr_value[1]];
+            END IF;
+
+        ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+            SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+        
+            -- See if we can skip the XSLT ... it's expensive
+            IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                -- Can't skip the transform
+                IF xfrm.xslt <> '---' THEN
+                    transformed_xml := oils_xslt_process(rmarc,xfrm.xslt);
+                ELSE
+                    transformed_xml := rmarc;
+                END IF;
+    
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            IF xfrm.name IS NULL THEN
+                -- just grab the marcxml (empty) transform
+                SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            FOR tmp_xml IN SELECT XPATH(attr_def.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]) LOOP
+                tmp_val := oils_xpath_string(
+                                '//*',
+                                tmp_xml::TEXT,
+                                COALESCE(attr_def.joiner,' '),
+                                ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
+                            );
+                IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
+                    attr_value := attr_value || tmp_val;
+                    EXIT WHEN NOT attr_def.multi;
+                END IF;
+            END LOOP;
+
+        ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+            SELECT  ARRAY_AGG(m.value) INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(rmarc) v
+                    LEFT JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
+              WHERE v.subfield = attr_def.phys_char_sf AND (m.value IS NOT NULL AND BTRIM(m.value) <> '')
+                    AND ( ccvm_row.id IS NULL OR ( ccvm_row.id IS NOT NULL AND v.id IS NOT NULL) );
+
+            IF NOT attr_def.multi THEN
+                attr_value := ARRAY[attr_value[1]];
+            END IF;
+
+        END IF;
+
+                -- apply index normalizers to attr_value
+        FOR tmp_val IN SELECT value FROM UNNEST(attr_value) x(value) LOOP
+            FOR normalizer IN
+                SELECT  n.func AS func,
+                        n.param_count AS param_count,
+                        m.params AS params
+                  FROM  config.index_normalizer n
+                        JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+                  WHERE attr = attr_def.name
+                  ORDER BY m.pos LOOP
+                    EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    COALESCE( quote_literal( tmp_val ), 'NULL' ) ||
+                        CASE
+                            WHEN normalizer.param_count > 0
+                                THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                                ELSE ''
+                            END ||
+                    ')' INTO tmp_val;
+
+            END LOOP;
+            IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
+                norm_attr_value := norm_attr_value || tmp_val;
+            END IF;
+        END LOOP;
+        
+        IF attr_def.filter THEN
+            -- Create unknown uncontrolled values and find the IDs of the values
+            IF ccvm_row.id IS NULL THEN
+                FOR tmp_val IN SELECT value FROM UNNEST(norm_attr_value) x(value) LOOP
+                    IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
+                        BEGIN -- use subtransaction to isolate unique constraint violations
+                            INSERT INTO metabib.uncontrolled_record_attr_value ( attr, value ) VALUES ( attr_def.name, tmp_val );
+                        EXCEPTION WHEN unique_violation THEN END;
+                    END IF;
+                END LOOP;
+
+                SELECT ARRAY_AGG(id) INTO attr_vector_tmp FROM metabib.uncontrolled_record_attr_value WHERE attr = attr_def.name AND value = ANY( norm_attr_value );
+            ELSE
+                SELECT ARRAY_AGG(id) INTO attr_vector_tmp FROM config.coded_value_map WHERE ctype = attr_def.name AND code = ANY( norm_attr_value );
+            END IF;
+
+            -- Add the new value to the vector
+            attr_vector := attr_vector || attr_vector_tmp;
+        END IF;
+
+        IF attr_def.sorter AND norm_attr_value[1] IS NOT NULL THEN
+            DELETE FROM metabib.record_sorter WHERE source = rid AND attr = attr_def.name;
+            INSERT INTO metabib.record_sorter (source, attr, value) VALUES (rid, attr_def.name, norm_attr_value[1]);
+        END IF;
+
+    END LOOP;
+
+/* We may need to rewrite the vlist to contain
+   the intersection of new values for requested
+   attrs and old values for ignored attrs. To
+   do this, we take the old attr vlist and
+   subtract any values that are valid for the
+   requested attrs, and then add back the new
+   set of attr values. */
+
+        IF ARRAY_LENGTH(pattr_list, 1) > 0 THEN 
+            SELECT vlist INTO attr_vector_tmp FROM metabib.record_attr_vector_list WHERE source = rid;
+            SELECT attr_vector_tmp - ARRAY_AGG(id::INT) INTO attr_vector_tmp FROM metabib.full_attr_id_map WHERE attr = ANY (pattr_list);
+            attr_vector := attr_vector || attr_vector_tmp;
+        END IF;
+
+    -- On to composite attributes, now that the record attrs have been pulled.  Processed in name order, so later composite
+    -- attributes can depend on earlier ones.
+    PERFORM metabib.compile_composite_attr_cache_init();
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE composite AND name = ANY( attr_list ) ORDER BY name LOOP
+
+        FOR ccvm_row IN SELECT * FROM config.coded_value_map c WHERE c.ctype = attr_def.name ORDER BY value LOOP
+
+            tmp_val := metabib.compile_composite_attr( ccvm_row.id );
+            CONTINUE WHEN tmp_val IS NULL OR tmp_val = ''; -- nothing to do
+
+            IF attr_def.filter THEN
+                IF attr_vector @@ tmp_val::query_int THEN
+                    attr_vector = attr_vector + intset(ccvm_row.id);
+                    EXIT WHEN NOT attr_def.multi;
+                END IF;
+            END IF;
+
+            IF attr_def.sorter THEN
+                IF attr_vector @@ tmp_val THEN
+                    DELETE FROM metabib.record_sorter WHERE source = rid AND attr = attr_def.name;
+                    INSERT INTO metabib.record_sorter (source, attr, value) VALUES (rid, attr_def.name, ccvm_row.code);
+                END IF;
+            END IF;
+
+        END LOOP;
+
+    END LOOP;
+
+    IF ARRAY_LENGTH(attr_vector, 1) > 0 THEN
+        IF rdeleted THEN -- initial insert OR revivication
+            DELETE FROM metabib.record_attr_vector_list WHERE source = rid;
+            INSERT INTO metabib.record_attr_vector_list (source, vlist) VALUES (rid, attr_vector);
+        ELSE
+            UPDATE metabib.record_attr_vector_list SET vlist = attr_vector WHERE source = rid;
+        END IF;
+    END IF;
+
+END;
+
+$func$ LANGUAGE PLPGSQL;
+
+-- AFTER UPDATE OR INSERT trigger for biblio.record_entry
+CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+BEGIN
+
+    IF NEW.deleted THEN -- If this bib is deleted
+        PERFORM * FROM config.internal_flag WHERE
+            name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled;
+        IF NOT FOUND THEN
+            -- One needs to keep these around to support searches
+            -- with the #deleted modifier, so one should turn on the named
+            -- internal flag for that functionality.
+            DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id;
+            DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id;
+        END IF;
+
+        DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
+        DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
+        RETURN NEW; -- and we're done
+            END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+    END IF;
+
+    -- Record authority linking
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
+    END IF;
+
+    -- Flatten and insert the mfr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM metabib.reingest_metabib_full_rec(NEW.id);
+
+        -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted);
+        END IF;
+    END IF;
+
+    -- Gather and insert the field entry data
+    PERFORM metabib.reingest_metabib_field_entries(NEW.id);
+
+    -- Located URI magic
+    IF TG_OP = 'INSERT' THEN
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    ELSE
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    END IF;
+
+    -- (re)map metarecord-bib linking
+    IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
+        IF NOT FOUND THEN
+            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 * 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;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION public.oils_tsearch2 () RETURNS TRIGGER AS $$
+DECLARE
+    normalizer      RECORD;
+    value           TEXT := '';
+    temp_vector     TEXT := '';
+    ts_rec          RECORD;
+    cur_weight      "char";
+BEGIN
+
+    value := NEW.value;
+    NEW.index_vector = ''::tsvector;
+
+    IF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
+              WHERE field = NEW.field AND m.pos < 0
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_literal( value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO value;
+
+        END LOOP;
+
+        NEW.value = value;
+
+        FOR normalizer IN
+            SELECT  n.func AS func,
+                    n.param_count AS param_count,
+                    m.params AS params
+              FROM  config.index_normalizer n
+                    JOIN config.metabib_field_index_norm_map m ON (m.norm = n.id)
+              WHERE field = NEW.field AND m.pos >= 0
+              ORDER BY m.pos LOOP
+                EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    quote_literal( value ) ||
+                    CASE
+                        WHEN normalizer.param_count > 0
+                            THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                            ELSE ''
+                        END ||
+                    ')' INTO value;
+
+        END LOOP;
+   END IF;
+
+    IF TG_TABLE_NAME::TEXT ~ 'browse_entry$' THEN
+
+        value :=  ARRAY_TO_STRING(
+            evergreen.regexp_split_to_array(value, E'\\W+'), ' '
+        );
+        value := public.search_normalize(value);
+        NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value);
+
+    ELSIF TG_TABLE_NAME::TEXT ~ 'field_entry$' THEN
+        FOR ts_rec IN
+
+            SELECT DISTINCT m.ts_config, m.index_weight
+            FROM config.metabib_class_ts_map m
+                 LEFT JOIN metabib.record_attr_vector_list r ON (r.source = NEW.source)
+                 LEFT JOIN config.coded_value_map ccvm ON (
+                    ccvm.ctype IN ('item_lang', 'language') AND
+                    ccvm.code = m.index_lang AND
+                    r.vlist @> intset(ccvm.id)
+                )
+            WHERE m.field_class = TG_ARGV[0]
+                AND m.active
+                AND (m.always OR NOT EXISTS (SELECT 1 FROM config.metabib_field_ts_map WHERE metabib_field = NEW.field))
+                AND (m.index_lang IS NULL OR ccvm.id IS NOT NULL)
+                        UNION
+            SELECT DISTINCT m.ts_config, m.index_weight
+            FROM config.metabib_field_ts_map m
+                 LEFT JOIN metabib.record_attr_vector_list r ON (r.source = NEW.source)
+                 LEFT JOIN config.coded_value_map ccvm ON (
+                    ccvm.ctype IN ('item_lang', 'language') AND
+                    ccvm.code = m.index_lang AND
+                    r.vlist @> intset(ccvm.id)
+                )
+            WHERE m.metabib_field = NEW.field
+                AND m.active
+                AND (m.index_lang IS NULL OR ccvm.id IS NOT NULL)
+            ORDER BY index_weight ASC
+
+        LOOP
+
+            IF cur_weight IS NOT NULL AND cur_weight != ts_rec.index_weight THEN
+                NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight);
+                temp_vector = '';
+            END IF;
+
+            cur_weight = ts_rec.index_weight;
+            SELECT INTO temp_vector temp_vector || ' ' || to_tsvector(ts_rec.ts_config::regconfig, value)::TEXT;
+
+        END LOOP;
+        NEW.index_vector = NEW.index_vector || setweight(temp_vector::tsvector,cur_weight);
+    ELSE
+        NEW.index_vector = to_tsvector(TG_ARGV[0]::regconfig, value);
+    END IF;
+
+    RETURN NEW;
+END;
+$$ LANGUAGE PLPGSQL;
+
+-- add new sr_format attribute definition
+
+INSERT INTO config.record_attr_definition (name, label, phys_char_sf)
+VALUES (
+    'sr_format', 
+    oils_i18n_gettext('sr_format', 'Sound recording format', 'crad', 'label'),
+    '62'
+);
+
+INSERT INTO config.coded_value_map (id, ctype, code, value) VALUES
+(557, 'sr_format', 'a', oils_i18n_gettext(557, '16 rpm', 'ccvm', 'value')),
+(558, 'sr_format', 'b', oils_i18n_gettext(558, '33 1/3 rpm', 'ccvm', 'value')),
+(559, 'sr_format', 'c', oils_i18n_gettext(559, '45 rpm', 'ccvm', 'value')),
+(560, 'sr_format', 'f', oils_i18n_gettext(560, '1.4 m. per second', 'ccvm', 'value')),
+(561, 'sr_format', 'd', oils_i18n_gettext(561, '78 rpm', 'ccvm', 'value')),
+(562, 'sr_format', 'e', oils_i18n_gettext(562, '8 rpm', 'ccvm', 'value')),
+(563, 'sr_format', 'l', oils_i18n_gettext(563, '1 7/8 ips', 'ccvm', 'value')),
+(586, 'item_form', 'o', oils_i18n_gettext('586', 'Online', 'ccvm', 'value')),
+(587, 'item_form', 'q', oils_i18n_gettext('587', 'Direct electronic', 'ccvm', 'value'));
+
+INSERT INTO config.coded_value_map
+    (id, ctype, code, value, search_label) VALUES 
+(564, 'icon_format', 'book', 
+    oils_i18n_gettext(564, 'Book', 'ccvm', 'value'),
+    oils_i18n_gettext(564, 'Book', 'ccvm', 'search_label')),
+(565, 'icon_format', 'braille', 
+    oils_i18n_gettext(565, 'Braille', 'ccvm', 'value'),
+    oils_i18n_gettext(565, 'Braille', 'ccvm', 'search_label')),
+(566, 'icon_format', 'software', 
+    oils_i18n_gettext(566, 'Software and video games', 'ccvm', 'value'),
+    oils_i18n_gettext(566, 'Software and video games', 'ccvm', 'search_label')),
+(567, 'icon_format', 'dvd', 
+    oils_i18n_gettext(567, 'DVD', 'ccvm', 'value'),
+    oils_i18n_gettext(567, 'DVD', 'ccvm', 'search_label')),
+(568, 'icon_format', 'ebook', 
+    oils_i18n_gettext(568, 'E-book', 'ccvm', 'value'),
+    oils_i18n_gettext(568, 'E-book', 'ccvm', 'search_label')),
+(569, 'icon_format', 'eaudio', 
+    oils_i18n_gettext(569, 'E-audio', 'ccvm', 'value'),
+    oils_i18n_gettext(569, 'E-audio', 'ccvm', 'search_label')),
+(570, 'icon_format', 'kit', 
+    oils_i18n_gettext(570, 'Kit', 'ccvm', 'value'),
+    oils_i18n_gettext(570, 'Kit', 'ccvm', 'search_label')),
+(571, 'icon_format', 'map', 
+    oils_i18n_gettext(571, 'Map', 'ccvm', 'value'),
+    oils_i18n_gettext(571, 'Map', 'ccvm', 'search_label')),
+(572, 'icon_format', 'microform', 
+    oils_i18n_gettext(572, 'Microform', 'ccvm', 'value'),
+    oils_i18n_gettext(572, 'Microform', 'ccvm', 'search_label')),
+(573, 'icon_format', 'score', 
+    oils_i18n_gettext(573, 'Music Score', 'ccvm', 'value'),
+    oils_i18n_gettext(573, 'Music Score', 'ccvm', 'search_label')),
+(574, 'icon_format', 'picture', 
+    oils_i18n_gettext(574, 'Picture', 'ccvm', 'value'),
+    oils_i18n_gettext(574, 'Picture', 'ccvm', 'search_label')),
+(575, 'icon_format', 'equip', 
+    oils_i18n_gettext(575, 'Equipment, games, toys', 'ccvm', 'value'),
+    oils_i18n_gettext(575, 'Equipment, games, toys', 'ccvm', 'search_label')),
+(576, 'icon_format', 'serial', 
+    oils_i18n_gettext(576, 'Serials and magazines', 'ccvm', 'value'),
+    oils_i18n_gettext(576, 'Serials and magazines', 'ccvm', 'search_label')),
+(577, 'icon_format', 'vhs', 
+    oils_i18n_gettext(577, 'VHS', 'ccvm', 'value'),
+    oils_i18n_gettext(577, 'VHS', 'ccvm', 'search_label')),
+(578, 'icon_format', 'evideo', 
+    oils_i18n_gettext(578, 'E-video', 'ccvm', 'value'),
+    oils_i18n_gettext(578, 'E-video', 'ccvm', 'search_label')),
+(579, 'icon_format', 'cdaudiobook', 
+    oils_i18n_gettext(579, 'CD Audiobook', 'ccvm', 'value'),
+    oils_i18n_gettext(579, 'CD Audiobook', 'ccvm', 'search_label')),
+(580, 'icon_format', 'cdmusic', 
+    oils_i18n_gettext(580, 'CD Music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(580, 'CD Music recording', 'ccvm', 'search_label')),
+(581, 'icon_format', 'casaudiobook', 
+    oils_i18n_gettext(581, 'Cassette audiobook', 'ccvm', 'value'),
+    oils_i18n_gettext(581, 'Cassette audiobook', 'ccvm', 'search_label')),
+(582, 'icon_format', 'casmusic',
+    oils_i18n_gettext(582, 'Audiocassette music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(582, 'Audiocassette music recording', 'ccvm', 'search_label')),
+(583, 'icon_format', 'phonospoken', 
+    oils_i18n_gettext(583, 'Phonograph spoken recording', 'ccvm', 'value'),
+    oils_i18n_gettext(583, 'Phonograph spoken recording', 'ccvm', 'search_label')),
+(584, 'icon_format', 'phonomusic', 
+    oils_i18n_gettext(584, 'Phonograph music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(584, 'Phonograph music recording', 'ccvm', 'search_label')),
+(585, 'icon_format', 'lpbook', 
+    oils_i18n_gettext(585, 'Large Print Book', 'ccvm', 'value'),
+    oils_i18n_gettext(585, 'Large Print Book', 'ccvm', 'search_label'))
+;
+
+-- add the new icon format attribute definition
+
+INSERT INTO config.global_flag (name, label, value, enabled) VALUES (
+    'opac.icon_attr',
+    oils_i18n_gettext(
+        'opac.icon_attr', 
+        'OPAC Format Icons Attribute',
+        'cgf',
+        'label'
+    ),
+    'icon_format', 
+    TRUE
+);
+
+INSERT INTO config.record_attr_definition 
+    (name, label, multi, filter, composite) VALUES (
+    'icon_format',
+    oils_i18n_gettext(
+        'icon_format',
+        'OPAC Format Icons',
+        'crad',
+        'label'
+    ),
+    TRUE, TRUE, TRUE
+);
+
+-- icon format composite definitions
+
+INSERT INTO config.composite_attr_entry_definition 
+    (coded_value, definition) VALUES
+--book
+(564, '{"0":[{"_attr":"item_type","_val":"a"},{"_attr":"item_type","_val":"t"}],"1":{"_not":[{"_attr":"item_form","_val":"a"},{"_attr":"item_form","_val":"b"},{"_attr":"item_form","_val":"c"},{"_attr":"item_form","_val":"d"},{"_attr":"item_form","_val":"f"},{"_attr":"item_form","_val":"o"},{"_attr":"item_form","_val":"q"},{"_attr":"item_form","_val":"r"},{"_attr":"item_form","_val":"s"}]},"2":[{"_attr":"bib_level","_val":"a"},{"_attr":"bib_level","_val":"c"},{"_attr":"bib_level","_val":"d"},{"_attr":"bib_level","_val":"m"}]}'),
+
+-- braille
+(565, '{"0":{"_attr":"item_type","_val":"a"},"1":{"_attr":"item_form","_val":"f"}}'),
+
+-- software
+(566, '{"_attr":"item_type","_val":"m"}'),
+
+-- dvd
+(567, '{"_attr":"vr_format","_val":"v"}'),
+
+-- ebook
+(568, '{"0":[{"_attr":"item_type","_val":"a"},{"_attr":"item_type","_val":"t"}],"1":[{"_attr":"item_form","_val":"o"},{"_attr":"item_form","_val":"s"},{"_attr":"item_form","_val":"q"}],"2":[{"_attr":"bib_level","_val":"a"},{"_attr":"bib_level","_val":"c"},{"_attr":"bib_level","_val":"d"},{"_attr":"bib_level","_val":"m"}]}'),
+
+-- eaudio
+(569, '{"0":{"_attr":"item_type","_val":"i"},"1":[{"_attr":"item_form","_val":"o"},{"_attr":"item_form","_val":"q"},{"_attr":"item_form","_val":"s"}]}'),
+
+-- kit
+(570, '[{"_attr":"item_type","_val":"o"},{"_attr":"item_type","_val":"p"}]'),
+
+-- map
+(571, '[{"_attr":"item_type","_val":"e"},{"_attr":"item_type","_val":"f"}]'),
+
+-- microform
+(572, '[{"_attr":"item_form","_val":"a"},{"_attr":"item_form","_val":"b"},{"_attr":"item_form","_val":"c"}]'),
+
+-- score
+(573, '[{"_attr":"item_type","_val":"c"},{"_attr":"item_type","_val":"d"}]'),
+
+-- picture
+(574, '{"_attr":"item_type","_val":"k"}'),
+
+-- equip
+(575, '{"_attr":"item_type","_val":"r"}'),
+
+-- serial
+(576, '[{"_attr":"bib_level","_val":"b"},{"_attr":"bib_level","_val":"s"}]'),
+
+-- vhs
+(577, '{"_attr":"vr_format","_val":"b"}'),
+
+-- evideo
+(578, '{"0":{"_attr":"item_type","_val":"g"},"1":[{"_attr":"item_form","_val":"o"},{"_attr":"item_form","_val":"s"},{"_attr":"item_form","_val":"q"}]}'),
+
+-- cdaudiobook
+(579, '{"0":{"_attr":"item_type","_val":"i"},"1":{"_attr":"sr_format","_val":"f"}}'),
+
+-- cdmusic
+(580, '{"0":{"_attr":"item_type","_val":"j"},"1":{"_attr":"sr_format","_val":"f"}}'),
+
+-- casaudiobook
+(581, '{"0":{"_attr":"item_type","_val":"i"},"1":{"_attr":"sr_format","_val":"l"}}'),
+
+-- casmusic
+(582, '{"0":{"_attr":"item_type","_val":"j"},"1":{"_attr":"sr_format","_val":"l"}}'),
+
+-- phonospoken
+(583, '{"0":{"_attr":"item_type","_val":"i"},"1":[{"_attr":"sr_format","_val":"a"},{"_attr":"sr_format","_val":"b"},{"_attr":"sr_format","_val":"c"},{"_attr":"sr_format","_val":"d"},{"_attr":"sr_format","_val":"e"}]}'),
+
+-- phonomusic
+(584, '{"0":{"_attr":"item_type","_val":"j"},"1":[{"_attr":"sr_format","_val":"a"},{"_attr":"sr_format","_val":"b"},{"_attr":"sr_format","_val":"c"},{"_attr":"sr_format","_val":"d"},{"_attr":"sr_format","_val":"e"}]}'),
+
+-- lpbook
+(585, '{"0":[{"_attr":"item_type","_val":"a"},{"_attr":"item_type","_val":"t"}],"1":{"_attr":"item_form","_val":"d"},"2":[{"_attr":"bib_level","_val":"a"},{"_attr":"bib_level","_val":"c"},{"_attr":"bib_level","_val":"d"},{"_attr":"bib_level","_val":"m"}]}');
+
+
+
+
+CREATE OR REPLACE FUNCTION unapi.mra (
+    obj_id BIGINT,
+    format TEXT,
+    ename TEXT,
+    includes TEXT[],
+    org TEXT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE
+) RETURNS XML AS $F$
+    SELECT  XMLELEMENT(
+        name attributes,
+        XMLATTRIBUTES(
+            CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
+            'tag:open-ils.org:U2@mra/' || $1 AS id, 
+            'tag:open-ils.org:U2@bre/' || $1 AS record 
+        ),  
+        (SELECT XMLAGG(foo.y)
+          FROM (
+            SELECT  XMLELEMENT(
+                        name field,
+                        XMLATTRIBUTES(
+                            mra.attr AS name,
+                            cvm.value AS "coded-value",
+                            cvm.id AS "cvmid",
+                            rad.composite,
+                            rad.multi,
+                            rad.filter,
+                            rad.sorter
+                        ),
+                        mra.value
+                    )
+              FROM  metabib.record_attr_flat mra
+                    JOIN config.record_attr_definition rad ON (mra.attr = rad.name)
+                    LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = mra.attr AND code = mra.value)
+              WHERE mra.id = $1
+            )foo(y)
+        )   
+    )   
+$F$ LANGUAGE SQL STABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0865', :eg_version);
+
+-- First, explode the field into constituent parts
+WITH format_parts_array AS (
+    SELECT  a.id,
+            STRING_TO_ARRAY(a.holdable_formats, '-') AS parts
+      FROM  action.hold_request a
+      WHERE a.hold_type = 'M'
+            AND a.fulfillment_time IS NULL
+), format_parts_wide AS (
+    SELECT  id,
+            regexp_split_to_array(parts[1], '') AS item_type,
+            regexp_split_to_array(parts[2], '') AS item_form,
+            parts[3] AS item_lang
+      FROM  format_parts_array
+), converted_formats_flat AS (
+    SELECT  id, 
+            CASE WHEN ARRAY_LENGTH(item_type,1) > 0
+                THEN '"0":[{"_attr":"item_type","_val":"' || ARRAY_TO_STRING(item_type,'"},{"_attr":"item_type","_val":"') || '"}]'
+                ELSE '"0":""'
+            END AS item_type,
+            CASE WHEN ARRAY_LENGTH(item_form,1) > 0
+                THEN '"1":[{"_attr":"item_form","_val":"' || ARRAY_TO_STRING(item_form,'"},{"_attr":"item_form","_val":"') || '"}]'
+                ELSE '"1":""'
+            END AS item_form,
+            CASE WHEN item_lang <> ''
+                THEN '"2":[{"_attr":"item_lang","_val":"' || item_lang ||'"}]'
+                ELSE '"2":""'
+            END AS item_lang
+      FROM  format_parts_wide
+) UPDATE action.hold_request SET holdable_formats = '{' ||
+        converted_formats_flat.item_type || ',' ||
+        converted_formats_flat.item_form || ',' ||
+        converted_formats_flat.item_lang || '}'
+    FROM converted_formats_flat WHERE converted_formats_flat.id = action.hold_request.id;
+
+SELECT evergreen.upgrade_deps_block_check('0866', :eg_version);
+
+DROP FUNCTION asset.record_has_holdable_copy (BIGINT);
+CREATE FUNCTION asset.record_has_holdable_copy ( rid BIGINT, ou INT DEFAULT NULL) RETURNS BOOL AS $f$
+BEGIN
+    PERFORM 1
+        FROM
+            asset.copy acp
+            JOIN asset.call_number acn ON acp.call_number = acn.id
+            JOIN asset.copy_location acpl ON acp.location = acpl.id
+            JOIN config.copy_status ccs ON acp.status = ccs.id
+        WHERE
+            acn.record = rid
+            AND acp.holdable = true
+            AND acpl.holdable = true
+            AND ccs.holdable = true
+            AND acp.deleted = false
+            AND acp.circ_lib IN (SELECT id FROM actor.org_unit_descendants(COALESCE($2,(SELECT id FROM evergreen.org_top()))))
+        LIMIT 1;
+    IF FOUND THEN
+        RETURN true;
+    END IF;
+    RETURN FALSE;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+DROP FUNCTION asset.metarecord_has_holdable_copy (BIGINT);
+CREATE FUNCTION asset.metarecord_has_holdable_copy ( rid BIGINT, ou INT DEFAULT NULL) RETURNS BOOL AS $f$
+BEGIN
+    PERFORM 1
+        FROM
+            asset.copy acp
+            JOIN asset.call_number acn ON acp.call_number = acn.id
+            JOIN asset.copy_location acpl ON acp.location = acpl.id
+            JOIN config.copy_status ccs ON acp.status = ccs.id
+            JOIN metabib.metarecord_source_map mmsm ON acn.record = mmsm.source
+        WHERE
+            mmsm.metarecord = rid
+            AND acp.holdable = true
+            AND acpl.holdable = true
+            AND ccs.holdable = true
+            AND acp.deleted = false
+            AND acp.circ_lib IN (SELECT id FROM actor.org_unit_descendants(COALESCE($2,(SELECT id FROM evergreen.org_top()))))
+        LIMIT 1;
+    IF FOUND THEN
+        RETURN true;
+    END IF;
+    RETURN FALSE;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.opac_ou_metarecord_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE
+    ans RECORD;
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) JOIN metabib.metarecord_source_map m ON (m.source = b.id) WHERE src.transcendant AND m.metarecord = rid;
+
+    FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
+        RETURN QUERY
+        SELECT  ans.depth,
+                ans.id,
+                COUNT( av.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( av.id ),
+                trans
+          FROM  
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.opac_visible_copies av ON (av.circ_lib = d.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
+                JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.opac_lasso_metarecord_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE
+    ans RECORD;
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) JOIN metabib.metarecord_source_map m ON (m.source = b.id) WHERE src.transcendant AND m.metarecord = rid;
+
+    FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
+        RETURN QUERY
+        SELECT  -1,
+                ans.id,
+                COUNT( av.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( av.id ),
+                trans
+          FROM
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.opac_visible_copies av ON (av.circ_lib = d.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
+                JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = av.record)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;   
+                
+    RETURN;     
+END;            
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.staff_ou_metarecord_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE         
+    ans RECORD; 
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) JOIN metabib.metarecord_source_map m ON (m.source = b.id) WHERE src.transcendant AND m.metarecord = rid;
+
+    FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
+        RETURN QUERY
+        SELECT  ans.depth,
+                ans.id,
+                COUNT( cp.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( cp.id ),
+                trans
+          FROM
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
+                JOIN asset.call_number cn ON (cn.id = cp.call_number AND NOT cn.deleted)
+                JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = cn.record)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.staff_lasso_metarecord_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE
+    ans RECORD;
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) JOIN metabib.metarecord_source_map m ON (m.source = b.id) WHERE src.transcendant AND m.metarecord = rid;
+
+    FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
+        RETURN QUERY
+        SELECT  -1,
+                ans.id,
+                COUNT( cp.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( cp.id ),
+                trans
+          FROM
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.copy cp ON (cp.circ_lib = d.id AND NOT cp.deleted)
+                JOIN asset.call_number cn ON (cn.id = cp.call_number AND NOT cn.deleted)
+                JOIN metabib.metarecord_source_map m ON (m.metarecord = rid AND m.source = cn.record)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION unapi.mmr_mra (
+    obj_id BIGINT,
+    format TEXT,
+    ename TEXT,
+    includes TEXT[],
+    org TEXT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+) RETURNS XML AS $F$
+    SELECT  XMLELEMENT(
+        name attributes,
+        XMLATTRIBUTES(
+            CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
+            'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
+        ),
+        (SELECT XMLAGG(foo.y)
+          FROM (
+            SELECT  DISTINCT ON (COALESCE(cvm.id,uvm.id))
+                    COALESCE(cvm.id,uvm.id),
+                    XMLELEMENT(
+                        name field,
+                        XMLATTRIBUTES(
+                            mra.attr AS name,
+                            cvm.value AS "coded-value",
+                            cvm.id AS "cvmid",
+                            rad.composite,
+                            rad.multi,
+                            rad.filter,
+                            rad.sorter
+                        ),
+                        mra.value
+                    )
+              FROM  metabib.record_attr_flat mra
+                    JOIN config.record_attr_definition rad ON (mra.attr = rad.name)
+                    LEFT JOIN config.coded_value_map cvm ON (cvm.ctype = mra.attr AND code = mra.value)
+                    LEFT JOIN metabib.uncontrolled_record_attr_value uvm ON (uvm.attr = mra.attr AND uvm.value = mra.value)
+              WHERE mra.id IN (
+                    WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id 
+                        FROM actor.org_unit WHERE shortname = $5 LIMIT 1)
+                    SELECT source 
+                    FROM metabib.metarecord_source_map, aou
+                    WHERE metarecord = $1 AND (
+                        EXISTS (
+                            SELECT 1 FROM asset.opac_visible_copies 
+                            WHERE record = source AND circ_lib IN (
+                                SELECT id FROM actor.org_unit_descendants(aou.id, $6)) 
+                            LIMIT 1
+                        )
+                        OR EXISTS (SELECT 1 FROM located_uris(source, aou.id, $10) LIMIT 1)
+                    )
+                )
+              ORDER BY 1
+            )foo(id,y)
+        )
+    )
+$F$ LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
+    bibid BIGINT[],
+    ouid INT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    pref_lib INT DEFAULT NULL,
+    includes TEXT[] DEFAULT NULL::TEXT[]
+) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
+    SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
+        SELECT acn.id, aou.name, acn.label_sortkey,
+            evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status),
+            RANK() OVER w
+        FROM asset.call_number acn
+            JOIN asset.copy acp ON (acn.id = acp.call_number)
+            JOIN actor.org_unit_descendants( $2, COALESCE(
+                $3, (
+                    SELECT depth
+                    FROM actor.org_unit_type aout
+                        INNER JOIN actor.org_unit ou ON ou_type = aout.id
+                    WHERE ou.id = $2
+                ), $6)
+            ) AS aou ON (acp.circ_lib = aou.id)
+        WHERE acn.record = ANY ($1)
+            AND acn.deleted IS FALSE
+            AND acp.deleted IS FALSE
+            AND CASE WHEN ('exclude_invisible_acn' = ANY($7)) THEN
+                EXISTS (
+                    SELECT 1
+                    FROM asset.opac_visible_copies
+                    WHERE copy_id = acp.id AND record = acn.record
+                ) ELSE TRUE END
+        GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
+        WINDOW w AS (
+            ORDER BY evergreen.rank_ou(aou.id, $2, $6), evergreen.rank_cp_status(acp.status)
+        )
+    ) AS ua
+    GROUP BY ua.id, ua.name, ua.label_sortkey
+    ORDER BY rank, ua.name, ua.label_sortkey
+    LIMIT ($4 -> 'acn')::INT
+    OFFSET ($5 -> 'acn')::INT;
+$$
+LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION evergreen.ranked_volumes
+    ( bibid BIGINT, ouid INT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, pref_lib INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[] )
+    RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT)
+    AS $$ SELECT * FROM evergreen.ranked_volumes(ARRAY[$1],$2,$3,$4,$5,$6,$7) $$ LANGUAGE SQL STABLE;
+
+
+CREATE OR REPLACE FUNCTION evergreen.located_uris (
+    bibid BIGINT[],
+    ouid INT,
+    pref_lib INT DEFAULT NULL
+) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
+    WITH all_orgs AS (SELECT COALESCE( enabled, FALSE ) AS flag FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy')
+    SELECT DISTINCT ON (id) * FROM (
+    SELECT acn.id, COALESCE(aou.name,aoud.name), acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
+      FROM asset.call_number acn
+           INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
+           INNER JOIN asset.uri auri ON auri.id = auricnm.uri
+           LEFT JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
+           LEFT JOIN actor.org_unit_descendants( COALESCE($3, $2) ) aoud ON (acn.owning_lib = aoud.id),
+           all_orgs
+      WHERE acn.record = ANY ($1)
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE
+          AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL)
+    UNION
+    SELECT acn.id, COALESCE(aou.name,aoud.name) AS name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
+      FROM asset.call_number acn
+           INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
+           INNER JOIN asset.uri auri ON auri.id = auricnm.uri
+           LEFT JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
+           LEFT JOIN actor.org_unit_descendants( $2 ) aoud ON (acn.owning_lib = aoud.id),
+           all_orgs
+      WHERE acn.record = ANY ($1)
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE
+          AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR COALESCE(aou.id,aoud.id) IS NOT NULL))x
+    ORDER BY id, pref_ou DESC;
+$$
+LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION evergreen.located_uris ( bibid BIGINT, ouid INT, pref_lib INT DEFAULT NULL)
+    RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT)
+    AS $$ SELECT * FROM evergreen.located_uris(ARRAY[$1],$2,$3) $$ LANGUAGE SQL STABLE;
+
+
+CREATE OR REPLACE FUNCTION unapi.mmr_holdings_xml (
+    mid BIGINT,
+    ouid INT,
+    org TEXT,
+    depth INT DEFAULT NULL,
+    includes TEXT[] DEFAULT NULL::TEXT[],
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+)
+RETURNS XML AS $F$
+     SELECT  XMLELEMENT(
+                 name holdings,
+                 XMLATTRIBUTES(
+                    CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    CASE WHEN ('mmr' = ANY ($5)) THEN 'tag:open-ils.org:U2@mmr/' || $1 || '/' || $3 ELSE NULL END AS id,
+                    (SELECT metarecord_has_holdable_copy FROM asset.metarecord_has_holdable_copy($1)) AS has_holdable
+                 ),
+                 XMLELEMENT(
+                     name counts,
+                     (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.opac_ou_metarecord_copy_count($2,  $1)
+                                     UNION
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.staff_ou_metarecord_copy_count($2, $1)
+                                     UNION
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.opac_ou_metarecord_copy_count($9,  $1)
+                                     ORDER BY 1
+                     )x)
+                 ),
+                 -- XXX monograph_parts and foreign_copies are skipped in MRs ... put them back some day?
+                 XMLELEMENT(
+                     name volumes,
+                     (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
+                        -- Physical copies
+                        SELECT  unapi.acn(y.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), y.rank, name, label_sortkey
+                        FROM evergreen.ranked_volumes((SELECT ARRAY_AGG(source) FROM metabib.metarecord_source_map WHERE metarecord = $1), $2, $4, $6, $7, $9, $5) AS y
+                        UNION ALL
+                        -- Located URIs
+                        SELECT unapi.acn(uris.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), uris.rank, name, label_sortkey
+                        FROM evergreen.located_uris((SELECT ARRAY_AGG(source) FROM metabib.metarecord_source_map WHERE metarecord = $1), $2, $9) AS uris
+                     )x)
+                 ),
+                 CASE WHEN ('ssub' = ANY ($5)) THEN
+                     XMLELEMENT(
+                         name subscriptions,
+                         (SELECT XMLAGG(ssub) FROM (
+                            SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
+                              FROM  serial.subscription
+                              WHERE record_entry IN (SELECT source FROM metabib.metarecord_source_map WHERE metarecord = $1)
+                        )x)
+                     )
+                 ELSE NULL END
+             );
+$F$ LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION unapi.mmr (
+    obj_id BIGINT,
+    format TEXT,
+    ename TEXT,
+    includes TEXT[],
+    org TEXT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+)
+RETURNS XML AS $F$
+DECLARE
+    mmrec   metabib.metarecord%ROWTYPE;
+    leadrec biblio.record_entry%ROWTYPE;
+    subrec biblio.record_entry%ROWTYPE;
+    layout  unapi.bre_output_layout%ROWTYPE;
+    xfrm    config.xml_transform%ROWTYPE;
+    ouid    INT;
+    xml_buf TEXT; -- growing XML document
+    tmp_xml TEXT; -- single-use XML string
+    xml_frag TEXT; -- single-use XML fragment
+    top_el  TEXT;
+    output  XML;
+    hxml    XML;
+    axml    XML;
+    subxml  XML; -- subordinate records elements
+    sub_xpath TEXT; 
+    parts   TEXT[]; 
+BEGIN
+
+    -- xpath for extracting bre.marc values from subordinate records 
+    -- so they may be appended to the MARC of the master record prior
+    -- to XSLT processing.
+    -- subjects, isbn, issn, upc -- anything else?
+    sub_xpath := 
+      '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]';
+
+    IF org = '-' OR org IS NULL THEN
+        SELECT shortname INTO org FROM evergreen.org_top();
+    END IF;
+
+    SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
+
+    IF ouid IS NULL THEN
+        RETURN NULL::XML;
+    END IF;
+
+    SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id;
+    IF NOT FOUND THEN
+        RETURN NULL::XML;
+    END IF;
+
+    -- TODO: aggregate holdings from constituent records
+    IF format = 'holdings_xml' THEN -- the special case
+        output := unapi.mmr_holdings_xml(
+            obj_id, ouid, org, depth,
+            evergreen.array_remove_item_by_value(includes,'holdings_xml'),
+            slimit, soffset, include_xmlns);
+        RETURN output;
+    END IF;
+
+    SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
+
+    IF layout.name IS NULL THEN
+        RETURN NULL::XML;
+    END IF;
+
+    SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
+
+    SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record;
+
+    -- Grab distinct MVF for all records if requested
+    IF ('mra' = ANY (includes)) THEN 
+        axml := unapi.mmr_mra(obj_id,NULL,NULL,NULL,org,depth,NULL,NULL,TRUE,pref_lib);
+    ELSE
+        axml := NULL::XML;
+    END IF;
+
+    xml_buf = leadrec.marc;
+
+    hxml := NULL::XML;
+    IF ('holdings_xml' = ANY (includes)) THEN
+        hxml := unapi.mmr_holdings_xml(
+                    obj_id, ouid, org, depth,
+                    evergreen.array_remove_item_by_value(includes,'holdings_xml'),
+                    slimit, soffset, include_xmlns, pref_lib);
+    END IF;
+
+    subxml := NULL::XML;
+    parts := '{}'::TEXT[];
+    FOR subrec IN SELECT bre.* FROM biblio.record_entry bre
+         JOIN metabib.metarecord_source_map mmsm ON (mmsm.source = bre.id)
+         JOIN metabib.metarecord mmr ON (mmr.id = mmsm.metarecord)
+         WHERE mmr.id = obj_id
+         ORDER BY CASE WHEN bre.id = mmr.master_record THEN 0 ELSE bre.id END
+         LIMIT COALESCE((slimit->'bre')::INT, 5) LOOP
+
+        IF subrec.id = leadrec.id THEN CONTINUE; END IF;
+        -- Append choice data from the the non-lead records to the 
+        -- the lead record document
+
+        parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[];
+    END LOOP;
+
+    SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p;
+
+    -- append data from the subordinate records to the 
+    -- main record document before applying the XSLT
+
+    IF subxml IS NOT NULL THEN 
+        xml_buf := REGEXP_REPLACE(xml_buf, 
+            '</record>(.*?)$', subxml || '</record>' || E'\\1');
+    END IF;
+
+    IF format = 'marcxml' THEN
+         -- If we're not using the prefixed namespace in 
+         -- this record, then remove all declarations of it
+        IF xml_buf !~ E'<marc:' THEN
+           xml_buf := REGEXP_REPLACE(xml_buf, 
+            ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
+        END IF; 
+    ELSE
+        xml_buf := oils_xslt_process(xml_buf, xfrm.xslt)::XML;
+    END IF;
+
+    -- update top_el to reflect the change in xml_buf, which may
+    -- now be a different type of document (e.g. record -> mods)
+    top_el := REGEXP_REPLACE(xml_buf, E'^.*?<((?:\\S+:)?' || 
+        layout.holdings_element || ').*$', E'\\1');
+
+    IF axml IS NOT NULL THEN 
+        xml_buf := REGEXP_REPLACE(xml_buf, 
+            '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
+    END IF;
+
+    IF hxml IS NOT NULL THEN
+        xml_buf := REGEXP_REPLACE(xml_buf, 
+            '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
+    END IF;
+
+    IF ('mmr.unapi' = ANY (includes)) THEN 
+        output := REGEXP_REPLACE(
+            xml_buf,
+            '</' || top_el || '>(.*?)',
+            XMLELEMENT(
+                name abbr,
+                XMLATTRIBUTES(
+                    'http://www.w3.org/1999/xhtml' AS xmlns,
+                    'unapi-id' AS class,
+                    'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title
+                )
+            )::TEXT || '</' || top_el || E'>\\1'
+        );
+    ELSE
+        output := xml_buf;
+    END IF;
+
+    -- remove ignorable whitesace
+    output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
+    RETURN output;
+END;
+$F$ LANGUAGE PLPGSQL STABLE;
+
+
+SELECT evergreen.upgrade_deps_block_check('0867', :eg_version);
+
+INSERT INTO config.global_flag (name, label, value, enabled) VALUES (
+    'opac.metarecord.holds.format_attr', 
+    oils_i18n_gettext(
+        'opac.metarecord.holds.format_attr',
+        'OPAC Metarecord Hold Formats Attribute', 
+        'cgf',
+        'label'
+    ),
+    'mr_hold_format', 
+    TRUE
+);
+
+-- until we have a custom attribute for the selector, 
+-- default to the icon_format attribute
+INSERT INTO config.global_flag (name, label, value, enabled) VALUES (
+    'opac.format_selector.attr', 
+    oils_i18n_gettext(
+        'opac.format_selector.attr', 
+        'OPAC Format Selector Attribute', 
+        'cgf',
+        'label'
+    ),
+    'icon_format', 
+    TRUE
+);
+
+
+INSERT INTO config.record_attr_definition 
+    (name, label, multi, filter, composite) 
+VALUES (
+    'mr_hold_format', 
+    oils_i18n_gettext(
+        'mr_hold_format',
+        'Metarecord Hold Formats', 
+        'crad',
+        'label'
+    ),
+    TRUE, TRUE, TRUE
+);
+
+-- these formats are a subset of the "icon_format" attribute,
+-- modified to exclude electronic resources, which are not holdable
+
+-- for i18n purposes, these have to be listed individually
+INSERT INTO config.coded_value_map
+    (id, ctype, code, value, search_label) VALUES 
+(588, 'mr_hold_format', 'book', 
+    oils_i18n_gettext(588, 'Book', 'ccvm', 'value'),
+    oils_i18n_gettext(588, 'Book', 'ccvm', 'search_label')),
+(589, 'mr_hold_format', 'braille', 
+    oils_i18n_gettext(589, 'Braille', 'ccvm', 'value'),
+    oils_i18n_gettext(589, 'Braille', 'ccvm', 'search_label')),
+(590, 'mr_hold_format', 'software', 
+    oils_i18n_gettext(590, 'Software and video games', 'ccvm', 'value'),
+    oils_i18n_gettext(590, 'Software and video games', 'ccvm', 'search_label')),
+(591, 'mr_hold_format', 'dvd', 
+    oils_i18n_gettext(591, 'DVD', 'ccvm', 'value'),
+    oils_i18n_gettext(591, 'DVD', 'ccvm', 'search_label')),
+(592, 'mr_hold_format', 'kit', 
+    oils_i18n_gettext(592, 'Kit', 'ccvm', 'value'),
+    oils_i18n_gettext(592, 'Kit', 'ccvm', 'search_label')),
+(593, 'mr_hold_format', 'map', 
+    oils_i18n_gettext(593, 'Map', 'ccvm', 'value'),
+    oils_i18n_gettext(593, 'Map', 'ccvm', 'search_label')),
+(594, 'mr_hold_format', 'microform', 
+    oils_i18n_gettext(594, 'Microform', 'ccvm', 'value'),
+    oils_i18n_gettext(594, 'Microform', 'ccvm', 'search_label')),
+(595, 'mr_hold_format', 'score', 
+    oils_i18n_gettext(595, 'Music Score', 'ccvm', 'value'),
+    oils_i18n_gettext(595, 'Music Score', 'ccvm', 'search_label')),
+(596, 'mr_hold_format', 'picture', 
+    oils_i18n_gettext(596, 'Picture', 'ccvm', 'value'),
+    oils_i18n_gettext(596, 'Picture', 'ccvm', 'search_label')),
+(597, 'mr_hold_format', 'equip', 
+    oils_i18n_gettext(597, 'Equipment, games, toys', 'ccvm', 'value'),
+    oils_i18n_gettext(597, 'Equipment, games, toys', 'ccvm', 'search_label')),
+(598, 'mr_hold_format', 'serial', 
+    oils_i18n_gettext(598, 'Serials and magazines', 'ccvm', 'value'),
+    oils_i18n_gettext(598, 'Serials and magazines', 'ccvm', 'search_label')),
+(599, 'mr_hold_format', 'vhs', 
+    oils_i18n_gettext(599, 'VHS', 'ccvm', 'value'),
+    oils_i18n_gettext(599, 'VHS', 'ccvm', 'search_label')),
+(600, 'mr_hold_format', 'cdaudiobook', 
+    oils_i18n_gettext(600, 'CD Audiobook', 'ccvm', 'value'),
+    oils_i18n_gettext(600, 'CD Audiobook', 'ccvm', 'search_label')),
+(601, 'mr_hold_format', 'cdmusic', 
+    oils_i18n_gettext(601, 'CD Music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(601, 'CD Music recording', 'ccvm', 'search_label')),
+(602, 'mr_hold_format', 'casaudiobook', 
+    oils_i18n_gettext(602, 'Cassette audiobook', 'ccvm', 'value'),
+    oils_i18n_gettext(602, 'Cassette audiobook', 'ccvm', 'search_label')),
+(603, 'mr_hold_format', 'casmusic',
+    oils_i18n_gettext(603, 'Audiocassette music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(603, 'Audiocassette music recording', 'ccvm', 'search_label')),
+(604, 'mr_hold_format', 'phonospoken', 
+    oils_i18n_gettext(604, 'Phonograph spoken recording', 'ccvm', 'value'),
+    oils_i18n_gettext(604, 'Phonograph spoken recording', 'ccvm', 'search_label')),
+(605, 'mr_hold_format', 'phonomusic', 
+    oils_i18n_gettext(605, 'Phonograph music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(605, 'Phonograph music recording', 'ccvm', 'search_label')),
+(606, 'mr_hold_format', 'lpbook', 
+    oils_i18n_gettext(606, 'Large Print Book', 'ccvm', 'value'),
+    oils_i18n_gettext(606, 'Large Print Book', 'ccvm', 'search_label'))
+;
+
+-- but we can auto-generate the composite definitions
+
+DO $$
+    DECLARE format TEXT;
+BEGIN
+    FOR format IN SELECT UNNEST(
+        '{book,braille,software,dvd,kit,map,microform,score,picture,equip,serial,vhs,cdaudiobook,cdmusic,casaudiobook,casmusic,phonospoken,phonomusic,lpbook}'::text[]) LOOP
+
+        INSERT INTO config.composite_attr_entry_definition 
+            (coded_value, definition) VALUES
+            (
+                -- get the ID from the new ccvm above
+                (SELECT id FROM config.coded_value_map 
+                    WHERE code = format AND ctype = 'mr_hold_format'),
+                -- get the def of the matching ccvm attached to the icon_format attr
+                (SELECT definition FROM config.composite_attr_entry_definition ccaed
+                    JOIN config.coded_value_map ccvm ON (ccaed.coded_value = ccvm.id)
+                    WHERE ccvm.ctype = 'icon_format' AND ccvm.code = format)
+            );
+    END LOOP; 
+END $$;
+
+INSERT INTO config.coded_value_map
+    (id, ctype, code, value, search_label) VALUES 
+(607, 'icon_format', 'music', 
+    oils_i18n_gettext(607, 'Musical Sound Recording (Unknown Format)', 'ccvm', 'value'),
+    oils_i18n_gettext(607, 'Musical Sound Recording (Unknown Format)', 'ccvm', 'search_label'));
+
+INSERT INTO config.composite_attr_entry_definition 
+    (coded_value, definition) VALUES
+(607, '{"0":{"_attr":"item_type","_val":"j"},"1":{"_not":[{"_attr":"sr_format","_val":"a"},{"_attr":"sr_format","_val":"b"},{"_attr":"sr_format","_val":"c"},{"_attr":"sr_format","_val":"d"},{"_attr":"sr_format","_val":"f"},{"_attr":"sr_format","_val":"e"},{"_attr":"sr_format","_val":"l"}]}}');
+
+-- icon for blu-ray
+INSERT INTO config.coded_value_map
+    (id, ctype, code, value, search_label) VALUES 
+(608, 'icon_format', 'blu-ray', 
+    oils_i18n_gettext(608, 'Blu-ray', 'ccvm', 'value'),
+    oils_i18n_gettext(608, 'Blu-ray', 'ccvm', 'search_label'));
+INSERT INTO config.composite_attr_entry_definition 
+    (coded_value, definition) VALUES (608, '{"_attr":"vr_format","_val":"s"}');
+
+-- metarecord hold format for blu-ray
+INSERT INTO config.coded_value_map
+    (id, ctype, code, value, search_label) VALUES 
+(609, 'mr_hold_format', 'blu-ray', 
+    oils_i18n_gettext(609, 'Blu-ray', 'ccvm', 'value'),
+    oils_i18n_gettext(609, 'Blu-ray', 'ccvm', 'search_label'));
+INSERT INTO config.composite_attr_entry_definition 
+    (coded_value, definition) VALUES (609, '{"_attr":"vr_format","_val":"s"}');
+
+-- add missing behind_desk column
+
+\qecho *** This ALTER TABLE might fail depending on your DB vintage. ***
+\qecho *** It should be harmless. ***
+ALTER TABLE action.aged_hold_request ADD COLUMN behind_desk BOOLEAN;
+
+SELECT evergreen.upgrade_deps_block_check('0868', :eg_version);
+
+CREATE OR REPLACE VIEW action.all_hold_request AS
+    SELECT DISTINCT
+           COALESCE(a.post_code, b.post_code) AS usr_post_code,
+           p.home_ou AS usr_home_ou,
+           p.profile AS usr_profile,
+           EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
+           CAST(ahr.requestor <> ahr.usr AS BOOLEAN) AS staff_placed,
+           ahr.id,
+           ahr.request_time,
+           ahr.capture_time,
+           ahr.fulfillment_time,
+           ahr.checkin_time,
+           ahr.return_time,
+           ahr.prev_check_time,
+           ahr.expire_time,
+           ahr.cancel_time,
+           ahr.cancel_cause,
+           ahr.cancel_note,
+           ahr.target,
+           ahr.current_copy,
+           ahr.fulfillment_staff,
+           ahr.fulfillment_lib,
+           ahr.request_lib,
+           ahr.selection_ou,
+           ahr.selection_depth,
+           ahr.pickup_lib,
+           ahr.hold_type,
+           ahr.holdable_formats,
+           CASE
+           WHEN ahr.phone_notify IS NULL THEN FALSE
+           WHEN ahr.phone_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS phone_notify,
+           ahr.email_notify,
+           CASE
+           WHEN ahr.sms_notify IS NULL THEN FALSE
+           WHEN ahr.sms_notify = '' THEN FALSE
+           ELSE TRUE
+           END AS sms_notify,
+           ahr.frozen,
+           ahr.thaw_date,
+           ahr.shelf_time,
+           ahr.cut_in_line,
+           ahr.mint_condition,
+           ahr.shelf_expire_time,
+           ahr.current_shelf_lib,
+           ahr.behind_desk
+    FROM action.hold_request ahr
+         JOIN actor.usr p ON (ahr.usr = p.id)
+         LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
+         LEFT JOIN actor.usr_address b ON (p.billing_address = b.id)
+    UNION ALL
+    SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib,
+           behind_desk
+    FROM action.aged_hold_request;
+
+
+
+CREATE OR REPLACE FUNCTION action.age_hold_on_delete () RETURNS TRIGGER AS $$
+DECLARE
+BEGIN
+    -- Archive a copy of the old row to action.aged_hold_request
+
+    INSERT INTO action.aged_hold_request
+           (usr_post_code,
+            usr_home_ou,
+            usr_profile,
+            usr_birth_year,
+            staff_placed,
+            id,
+            request_time,
+            capture_time,
+            fulfillment_time,
+            checkin_time,
+            return_time,
+            prev_check_time,
+            expire_time,
+            cancel_time,
+            cancel_cause,
+            cancel_note,
+            target,
+            current_copy,
+            fulfillment_staff,
+            fulfillment_lib,
+            request_lib,
+            selection_ou,
+            selection_depth,
+            pickup_lib,
+            hold_type,
+            holdable_formats,
+            phone_notify,
+            email_notify,
+            sms_notify,
+            frozen,
+            thaw_date,
+            shelf_time,
+            cut_in_line,
+            mint_condition,
+            shelf_expire_time,
+            current_shelf_lib,
+            behind_desk)
+      SELECT 
+           usr_post_code,
+           usr_home_ou,
+           usr_profile,
+           usr_birth_year,
+           staff_placed,
+           id,
+           request_time,
+           capture_time,
+           fulfillment_time,
+           checkin_time,
+           return_time,
+           prev_check_time,
+           expire_time,
+           cancel_time,
+           cancel_cause,
+           cancel_note,
+           target,
+           current_copy,
+           fulfillment_staff,
+           fulfillment_lib,
+           request_lib,
+           selection_ou,
+           selection_depth,
+           pickup_lib,
+           hold_type,
+           holdable_formats,
+           phone_notify,
+           email_notify,
+           sms_notify,
+           frozen,
+           thaw_date,
+           shelf_time,
+           cut_in_line,
+           mint_condition,
+           shelf_expire_time,
+           current_shelf_lib,
+           behind_desk
+        FROM action.all_hold_request WHERE id = OLD.id;
+
+    RETURN OLD;
+END;
+$$ LANGUAGE 'plpgsql';
+
+SELECT evergreen.upgrade_deps_block_check('0869', :eg_version);
+
+CREATE OR REPLACE FUNCTION action.hold_copy_calculated_proximity_update () RETURNS TRIGGER AS $f$
+BEGIN
+    NEW.proximity := action.hold_copy_calculated_proximity(NEW.hold,NEW.target_copy);
+    RETURN NEW;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER hold_copy_proximity_update_tgr BEFORE INSERT OR UPDATE ON action.hold_copy_map FOR EACH ROW EXECUTE PROCEDURE action.hold_copy_calculated_proximity_update ();
+
+-- Now, cause the update we need in a HOT-friendly manner (http://pgsql.tapoueh.org/site/html/misc/hot.html)
+UPDATE action.hold_copy_map SET proximity = proximity WHERE proximity IS NULL;
+
+SELECT evergreen.upgrade_deps_block_check('0870', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.located_uris (
+    bibid BIGINT[],
+    ouid INT,
+    pref_lib INT DEFAULT NULL
+) RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank INT) AS $$
+    WITH all_orgs AS (SELECT COALESCE( enabled, FALSE ) AS flag FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy')
+    SELECT DISTINCT ON (id) * FROM (
+    SELECT acn.id, COALESCE(aou.name,aoud.name), acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
+      FROM asset.call_number acn
+           INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
+           INNER JOIN asset.uri auri ON auri.id = auricnm.uri
+           LEFT JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
+           LEFT JOIN actor.org_unit_descendants( COALESCE($3, $2) ) aoud ON (acn.owning_lib = aoud.id),
+           all_orgs
+      WHERE acn.record = ANY ($1)
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE
+          AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR (all_orgs.flag AND COALESCE(aou.id,aoud.id) IS NOT NULL))
+    UNION
+    SELECT acn.id, COALESCE(aou.name,aoud.name) AS name, acn.label_sortkey, evergreen.rank_ou(aou.id, $2, $3) AS pref_ou
+      FROM asset.call_number acn
+           INNER JOIN asset.uri_call_number_map auricnm ON acn.id = auricnm.call_number
+           INNER JOIN asset.uri auri ON auri.id = auricnm.uri
+           LEFT JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
+           LEFT JOIN actor.org_unit_descendants( $2 ) aoud ON (acn.owning_lib = aoud.id),
+           all_orgs
+      WHERE acn.record = ANY ($1)
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE
+          AND ((NOT all_orgs.flag AND aou.id IS NOT NULL) OR (all_orgs.flag AND COALESCE(aou.id,aoud.id) IS NOT NULL)))x
+    ORDER BY id, pref_ou DESC;
+$$
+LANGUAGE SQL STABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0871', :eg_version);
+
+INSERT INTO config.record_attr_definition 
+    (name, label, multi, filter, composite) VALUES (
+        'search_format', 
+        oils_i18n_gettext('search_format', 'Search Formats', 'crad', 'label'),
+        TRUE, TRUE, TRUE
+    );
+
+INSERT INTO config.coded_value_map
+    (id, ctype, code, value, search_label) VALUES 
+(610, 'search_format', 'book', 
+    oils_i18n_gettext(610, 'All Books', 'ccvm', 'value'),
+    oils_i18n_gettext(610, 'All Books', 'ccvm', 'search_label')),
+(611, 'search_format', 'braille', 
+    oils_i18n_gettext(611, 'Braille', 'ccvm', 'value'),
+    oils_i18n_gettext(611, 'Braille', 'ccvm', 'search_label')),
+(612, 'search_format', 'software', 
+    oils_i18n_gettext(612, 'Software and video games', 'ccvm', 'value'),
+    oils_i18n_gettext(612, 'Software and video games', 'ccvm', 'search_label')),
+(613, 'search_format', 'dvd', 
+    oils_i18n_gettext(613, 'DVD', 'ccvm', 'value'),
+    oils_i18n_gettext(613, 'DVD', 'ccvm', 'search_label')),
+(614, 'search_format', 'ebook', 
+    oils_i18n_gettext(614, 'E-book', 'ccvm', 'value'),
+    oils_i18n_gettext(614, 'E-book', 'ccvm', 'search_label')),
+(615, 'search_format', 'eaudio', 
+    oils_i18n_gettext(615, 'E-audio', 'ccvm', 'value'),
+    oils_i18n_gettext(615, 'E-audio', 'ccvm', 'search_label')),
+(616, 'search_format', 'kit', 
+    oils_i18n_gettext(616, 'Kit', 'ccvm', 'value'),
+    oils_i18n_gettext(616, 'Kit', 'ccvm', 'search_label')),
+(617, 'search_format', 'map', 
+    oils_i18n_gettext(617, 'Map', 'ccvm', 'value'),
+    oils_i18n_gettext(617, 'Map', 'ccvm', 'search_label')),
+(618, 'search_format', 'microform', 
+    oils_i18n_gettext(618, 'Microform', 'ccvm', 'value'),
+    oils_i18n_gettext(618, 'Microform', 'ccvm', 'search_label')),
+(619, 'search_format', 'score', 
+    oils_i18n_gettext(619, 'Music Score', 'ccvm', 'value'),
+    oils_i18n_gettext(619, 'Music Score', 'ccvm', 'search_label')),
+(620, 'search_format', 'picture', 
+    oils_i18n_gettext(620, 'Picture', 'ccvm', 'value'),
+    oils_i18n_gettext(620, 'Picture', 'ccvm', 'search_label')),
+(621, 'search_format', 'equip', 
+    oils_i18n_gettext(621, 'Equipment, games, toys', 'ccvm', 'value'),
+    oils_i18n_gettext(621, 'Equipment, games, toys', 'ccvm', 'search_label')),
+(622, 'search_format', 'serial', 
+    oils_i18n_gettext(622, 'Serials and magazines', 'ccvm', 'value'),
+    oils_i18n_gettext(622, 'Serials and magazines', 'ccvm', 'search_label')),
+(623, 'search_format', 'vhs', 
+    oils_i18n_gettext(623, 'VHS', 'ccvm', 'value'),
+    oils_i18n_gettext(623, 'VHS', 'ccvm', 'search_label')),
+(624, 'search_format', 'evideo', 
+    oils_i18n_gettext(624, 'E-video', 'ccvm', 'value'),
+    oils_i18n_gettext(624, 'E-video', 'ccvm', 'search_label')),
+(625, 'search_format', 'cdaudiobook', 
+    oils_i18n_gettext(625, 'CD Audiobook', 'ccvm', 'value'),
+    oils_i18n_gettext(625, 'CD Audiobook', 'ccvm', 'search_label')),
+(626, 'search_format', 'cdmusic', 
+    oils_i18n_gettext(626, 'CD Music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(626, 'CD Music recording', 'ccvm', 'search_label')),
+(627, 'search_format', 'casaudiobook', 
+    oils_i18n_gettext(627, 'Cassette audiobook', 'ccvm', 'value'),
+    oils_i18n_gettext(627, 'Cassette audiobook', 'ccvm', 'search_label')),
+(628, 'search_format', 'casmusic',
+    oils_i18n_gettext(628, 'Audiocassette music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(628, 'Audiocassette music recording', 'ccvm', 'search_label')),
+(629, 'search_format', 'phonospoken', 
+    oils_i18n_gettext(629, 'Phonograph spoken recording', 'ccvm', 'value'),
+    oils_i18n_gettext(629, 'Phonograph spoken recording', 'ccvm', 'search_label')),
+(630, 'search_format', 'phonomusic', 
+    oils_i18n_gettext(630, 'Phonograph music recording', 'ccvm', 'value'),
+    oils_i18n_gettext(630, 'Phonograph music recording', 'ccvm', 'search_label')),
+(631, 'search_format', 'lpbook', 
+    oils_i18n_gettext(631, 'Large Print Book', 'ccvm', 'value'),
+    oils_i18n_gettext(631, 'Large Print Book', 'ccvm', 'search_label')),
+(632, 'search_format', 'music', 
+    oils_i18n_gettext(632, 'All Music', 'ccvm', 'label'),
+    oils_i18n_gettext(632, 'All Music', 'ccvm', 'search_label')),
+(633, 'search_format', 'blu-ray', 
+    oils_i18n_gettext(633, 'Blu-ray', 'ccvm', 'value'),
+    oils_i18n_gettext(633, 'Blu-ray', 'ccvm', 'search_label'));
+
+
+
+-- copy the composite definition from icon_format into 
+-- search_format for a baseline data set
+DO $$
+    DECLARE format config.coded_value_map%ROWTYPE;
+BEGIN
+    FOR format IN SELECT * 
+        FROM config.coded_value_map WHERE ctype = 'icon_format'
+    LOOP
+        INSERT INTO config.composite_attr_entry_definition 
+            (coded_value, definition) VALUES
+            (
+                -- get the ID from the new ccvm above
+                (SELECT id FROM config.coded_value_map 
+                    WHERE code = format.code AND ctype = 'search_format'),
+
+                -- def of the matching icon_format attr
+                (SELECT definition FROM config.composite_attr_entry_definition 
+                    WHERE coded_value = format.id)
+            );
+    END LOOP; 
+END $$;
+
+-- modify the 'book' definition so that it includes large print
+UPDATE config.composite_attr_entry_definition 
+    SET definition = '{"0":[{"_attr":"item_type","_val":"a"},{"_attr":"item_type","_val":"t"}],"1":{"_not":[{"_attr":"item_form","_val":"a"},{"_attr":"item_form","_val":"b"},{"_attr":"item_form","_val":"c"},{"_attr":"item_form","_val":"f"},{"_attr":"item_form","_val":"o"},{"_attr":"item_form","_val":"q"},{"_attr":"item_form","_val":"r"},{"_attr":"item_form","_val":"s"}]},"2":[{"_attr":"bib_level","_val":"a"},{"_attr":"bib_level","_val":"c"},{"_attr":"bib_level","_val":"d"},{"_attr":"bib_level","_val":"m"}]}'
+    WHERE coded_value = 610;
+
+-- modify 'music' to include all recorded music, regardless of format
+UPDATE config.composite_attr_entry_definition 
+    SET definition = '{"_attr":"item_type","_val":"j"}'
+    WHERE coded_value = 632;
+
+UPDATE config.global_flag 
+    SET value = 'search_format' 
+    WHERE name = 'opac.format_selector.attr';
+
+SELECT evergreen.upgrade_deps_block_check('0872', :eg_version);
+
+CREATE OR REPLACE FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT, bib_is_deleted BOOL DEFAULT FALSE, retain_deleted BOOL DEFAULT FALSE ) RETURNS BIGINT AS $func$
+DECLARE
+    new_mapping     BOOL := TRUE;
+    source_count    INT;
+    old_mr          BIGINT;
+    tmp_mr          metabib.metarecord%ROWTYPE;
+    deleted_mrs     BIGINT[];
+BEGIN
+
+    -- We need to make sure we're not a deleted master record of an MR
+    IF bib_is_deleted THEN
+        FOR old_mr IN SELECT id FROM metabib.metarecord WHERE master_record = bib_id LOOP
+
+            IF NOT retain_deleted THEN -- Go away for any MR that we're master of, unless retained
+                DELETE FROM metabib.metarecord_source_map WHERE source = bib_id;
+            END IF;
+
+            -- Now, are there any more sources on this MR?
+            SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = old_mr;
+
+            IF source_count = 0 AND NOT retain_deleted THEN -- No other records
+                deleted_mrs := ARRAY_APPEND(deleted_mrs, old_mr); -- Just in case...
+                DELETE FROM metabib.metarecord WHERE id = old_mr;
+
+            ELSE -- indeed there are. Update it with a null cache and recalcualated master record
+                UPDATE  metabib.metarecord
+                  SET   mods = NULL,
+                        master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
+                  WHERE id = old_mr;
+            END IF;
+        END LOOP;
+
+    ELSE -- insert or update
+
+        FOR tmp_mr IN SELECT m.* FROM metabib.metarecord m JOIN metabib.metarecord_source_map s ON (s.metarecord = m.id) WHERE s.source = bib_id LOOP
+
+            -- Find the first fingerprint-matching
+            IF old_mr IS NULL AND fp = tmp_mr.fingerprint THEN
+                old_mr := tmp_mr.id;
+                new_mapping := FALSE;
+
+            ELSE -- Our fingerprint changed ... maybe remove the old MR
+                DELETE FROM metabib.metarecord_source_map WHERE metarecord = old_mr AND source = bib_id; -- remove the old source mapping
+                SELECT COUNT(*) INTO source_count FROM metabib.metarecord_source_map WHERE metarecord = tmp_mr.id;
+                IF source_count = 0 THEN -- No other records
+                    deleted_mrs := ARRAY_APPEND(deleted_mrs, tmp_mr.id);
+                    DELETE FROM metabib.metarecord WHERE id = tmp_mr.id;
+                END IF;
+            END IF;
+
+        END LOOP;
+
+        -- we found no suitable, preexisting MR based on old source maps
+        IF old_mr IS NULL THEN
+            SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp; -- is there one for our current fingerprint?
+
+            IF old_mr IS NULL THEN -- nope, create one and grab its id
+                INSERT INTO metabib.metarecord ( fingerprint, master_record ) VALUES ( fp, bib_id );
+                SELECT id INTO old_mr FROM metabib.metarecord WHERE fingerprint = fp;
+
+            ELSE -- indeed there is. update it with a null cache and recalcualated master record
+                UPDATE  metabib.metarecord
+                  SET   mods = NULL,
+                        master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
+                  WHERE id = old_mr;
+            END IF;
+
+        ELSE -- there was one we already attached to, update its mods cache and master_record
+            UPDATE  metabib.metarecord
+              SET   mods = NULL,
+                    master_record = ( SELECT id FROM biblio.record_entry WHERE fingerprint = fp AND NOT deleted ORDER BY quality DESC LIMIT 1)
+              WHERE id = old_mr;
+        END IF;
+
+        IF new_mapping THEN
+            INSERT INTO metabib.metarecord_source_map (metarecord, source) VALUES (old_mr, bib_id); -- new source mapping
+        END IF;
+
+    END IF;
+
+    IF ARRAY_UPPER(deleted_mrs,1) > 0 THEN
+        UPDATE action.hold_request SET target = old_mr WHERE target IN ( SELECT unnest(deleted_mrs) ) AND hold_type = 'M'; -- if we had to delete any MRs above, make sure their holds are moved
+    END IF;
+
+    RETURN old_mr;
+
+END;
+$func$ LANGUAGE PLPGSQL;
+
+DROP FUNCTION metabib.remap_metarecord_for_bib( bib_id BIGINT, fp TEXT );
+
+CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+DECLARE
+    tmp_bool BOOL;
+BEGIN
+
+    IF NEW.deleted THEN -- If this bib is deleted
+
+        PERFORM * FROM config.internal_flag WHERE
+            name = 'ingest.metarecord_mapping.preserve_on_delete' AND enabled;
+
+        tmp_bool := FOUND; -- Just in case this is changed by some other statement
+
+        PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint, TRUE, tmp_bool );
+
+        IF NOT tmp_bool THEN
+            -- One needs to keep these around to support searches
+            -- with the #deleted modifier, so one should turn on the named
+            -- internal flag for that functionality.
+            DELETE FROM metabib.record_attr_vector_list WHERE source = NEW.id;
+        END IF;
+
+        DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = NEW.id; -- Separate any multi-homed items
+        DELETE FROM metabib.browse_entry_def_map WHERE source = NEW.id; -- Don't auto-suggest deleted bibs
+        RETURN NEW; -- and we're done
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+    END IF;
+
+    -- Record authority linking
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
+    END IF;
+
+    -- Flatten and insert the mfr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM metabib.reingest_metabib_full_rec(NEW.id);
+
+        -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM metabib.reingest_record_attributes(NEW.id, NULL, NEW.marc, TG_OP = 'INSERT' OR OLD.deleted);
+        END IF;
+    END IF;
+
+    -- Gather and insert the field entry data
+    PERFORM metabib.reingest_metabib_field_entries(NEW.id);
+
+    -- Located URI magic
+    IF TG_OP = 'INSERT' THEN
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    ELSE
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    END IF;
+
+    -- (re)map metarecord-bib linking
+    IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
+        IF NOT FOUND THEN
+            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 * 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;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION unapi.mmr (
+    obj_id BIGINT,
+    format TEXT,
+    ename TEXT,
+    includes TEXT[],
+    org TEXT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+)
+RETURNS XML AS $F$
+DECLARE
+    mmrec   metabib.metarecord%ROWTYPE;
+    leadrec biblio.record_entry%ROWTYPE;
+    subrec biblio.record_entry%ROWTYPE;
+    layout  unapi.bre_output_layout%ROWTYPE;
+    xfrm    config.xml_transform%ROWTYPE;
+    ouid    INT;
+    xml_buf TEXT; -- growing XML document
+    tmp_xml TEXT; -- single-use XML string
+    xml_frag TEXT; -- single-use XML fragment
+    top_el  TEXT;
+    output  XML;
+    hxml    XML;
+    axml    XML;
+    subxml  XML; -- subordinate records elements
+    sub_xpath TEXT; 
+    parts   TEXT[]; 
+BEGIN
+
+    -- xpath for extracting bre.marc values from subordinate records 
+    -- so they may be appended to the MARC of the master record prior
+    -- to XSLT processing.
+    -- subjects, isbn, issn, upc -- anything else?
+    sub_xpath := 
+      '//*[starts-with(@tag, "6") or @tag="020" or @tag="022" or @tag="024"]';
+
+    IF org = '-' OR org IS NULL THEN
+        SELECT shortname INTO org FROM evergreen.org_top();
+    END IF;
+
+    SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
+
+    IF ouid IS NULL THEN
+        RETURN NULL::XML;
+    END IF;
+
+    SELECT INTO mmrec * FROM metabib.metarecord WHERE id = obj_id;
+    IF NOT FOUND THEN
+        RETURN NULL::XML;
+    END IF;
+
+    -- TODO: aggregate holdings from constituent records
+    IF format = 'holdings_xml' THEN -- the special case
+        output := unapi.mmr_holdings_xml(
+            obj_id, ouid, org, depth,
+            evergreen.array_remove_item_by_value(includes,'holdings_xml'),
+            slimit, soffset, include_xmlns, pref_lib);
+        RETURN output;
+    END IF;
+
+    SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
+
+    IF layout.name IS NULL THEN
+        RETURN NULL::XML;
+    END IF;
+
+    SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
+
+    SELECT INTO leadrec * FROM biblio.record_entry WHERE id = mmrec.master_record;
+
+    -- Grab distinct MVF for all records if requested
+    IF ('mra' = ANY (includes)) THEN 
+        axml := unapi.mmr_mra(obj_id,NULL,NULL,NULL,org,depth,NULL,NULL,TRUE,pref_lib);
+    ELSE
+        axml := NULL::XML;
+    END IF;
+
+    xml_buf = leadrec.marc;
+
+    hxml := NULL::XML;
+    IF ('holdings_xml' = ANY (includes)) THEN
+        hxml := unapi.mmr_holdings_xml(
+                    obj_id, ouid, org, depth,
+                    evergreen.array_remove_item_by_value(includes,'holdings_xml'),
+                    slimit, soffset, include_xmlns, pref_lib);
+    END IF;
+
+    subxml := NULL::XML;
+    parts := '{}'::TEXT[];
+    FOR subrec IN SELECT bre.* FROM biblio.record_entry bre
+         JOIN metabib.metarecord_source_map mmsm ON (mmsm.source = bre.id)
+         JOIN metabib.metarecord mmr ON (mmr.id = mmsm.metarecord)
+         WHERE mmr.id = obj_id AND NOT bre.deleted
+         ORDER BY CASE WHEN bre.id = mmr.master_record THEN 0 ELSE bre.id END
+         LIMIT COALESCE((slimit->'bre')::INT, 5) LOOP
+
+        IF subrec.id = leadrec.id THEN CONTINUE; END IF;
+        -- Append choice data from the the non-lead records to the 
+        -- the lead record document
+
+        parts := parts || xpath(sub_xpath, subrec.marc::XML)::TEXT[];
+    END LOOP;
+
+    SELECT ARRAY_TO_STRING( ARRAY_AGG( DISTINCT p ), '' )::XML INTO subxml FROM UNNEST(parts) p;
+
+    -- append data from the subordinate records to the 
+    -- main record document before applying the XSLT
+
+    IF subxml IS NOT NULL THEN 
+        xml_buf := REGEXP_REPLACE(xml_buf, 
+            '</record>(.*?)$', subxml || '</record>' || E'\\1');
+    END IF;
+
+    IF format = 'marcxml' THEN
+         -- If we're not using the prefixed namespace in 
+         -- this record, then remove all declarations of it
+        IF xml_buf !~ E'<marc:' THEN
+           xml_buf := REGEXP_REPLACE(xml_buf, 
+            ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
+        END IF; 
+    ELSE
+        xml_buf := oils_xslt_process(xml_buf, xfrm.xslt)::XML;
+    END IF;
+
+    -- update top_el to reflect the change in xml_buf, which may
+    -- now be a different type of document (e.g. record -> mods)
+    top_el := REGEXP_REPLACE(xml_buf, E'^.*?<((?:\\S+:)?' || 
+        layout.holdings_element || ').*$', E'\\1');
+
+    IF axml IS NOT NULL THEN 
+        xml_buf := REGEXP_REPLACE(xml_buf, 
+            '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
+    END IF;
+
+    IF hxml IS NOT NULL THEN
+        xml_buf := REGEXP_REPLACE(xml_buf, 
+            '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
+    END IF;
+
+    IF ('mmr.unapi' = ANY (includes)) THEN 
+        output := REGEXP_REPLACE(
+            xml_buf,
+            '</' || top_el || '>(.*?)',
+            XMLELEMENT(
+                name abbr,
+                XMLATTRIBUTES(
+                    'http://www.w3.org/1999/xhtml' AS xmlns,
+                    'unapi-id' AS class,
+                    'tag:open-ils.org:U2@mmr/' || obj_id || '/' || org AS title
+                )
+            )::TEXT || '</' || top_el || E'>\\1'
+        );
+    ELSE
+        output := xml_buf;
+    END IF;
+
+    -- remove ignorable whitesace
+    output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
+    RETURN output;
+END;
+$F$ LANGUAGE PLPGSQL STABLE;
+
+\qecho Remapping deleted master records
+-- Forcibly remap deleted master records, retaining the linkage if so configured.
+SELECT  count(metabib.remap_metarecord_for_bib( bre.id, bre.fingerprint, TRUE, COALESCE(flag.enabled,FALSE)))
+  FROM  metabib.metarecord metar
+        JOIN biblio.record_entry bre ON bre.id = metar.master_record,
+        config.internal_flag flag
+  WHERE bre.deleted = TRUE AND flag.name = 'ingest.metarecord_mapping.preserve_on_delete';
+
+SELECT evergreen.upgrade_deps_block_check('0873', :eg_version);
+
+CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint(pickup_ou integer, request_ou integer, match_item bigint, match_user integer, match_requestor integer)
+  RETURNS integer AS
+$func$
+DECLARE
+    requestor_object    actor.usr%ROWTYPE;
+    user_object         actor.usr%ROWTYPE;
+    item_object         asset.copy%ROWTYPE;
+    item_cn_object      asset.call_number%ROWTYPE;
+    my_item_age         INTERVAL;
+    rec_descriptor      metabib.rec_descriptor%ROWTYPE;
+    matchpoint          config.hold_matrix_matchpoint%ROWTYPE;
+    weights             config.hold_matrix_weights%ROWTYPE;
+    denominator         NUMERIC(6,2);
+    v_pickup_ou         ALIAS FOR pickup_ou;
+    v_request_ou         ALIAS FOR request_ou;
+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      * FROM metabib.rec_descriptor   WHERE record = item_cn_object.record;
+
+    SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
+
+    -- The item's owner should probably be the one determining if the item is holdable
+    -- How to decide that is debatable. Decided to default to the circ library (where the item lives)
+    -- This flag will allow for setting it to the owning library (where the call number "lives")
+    PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.weight_owner_not_circ' AND enabled;
+
+    -- Grab the closest set circ weight setting.
+    IF NOT FOUND THEN
+        -- Default to circ library
+        SELECT INTO weights hw.*
+          FROM config.weight_assoc wa
+               JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
+               JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) d ON (wa.org_unit = d.id)
+          WHERE active
+          ORDER BY d.distance
+          LIMIT 1;
+    ELSE
+        -- Flag is set, use owning library
+        SELECT INTO weights hw.*
+          FROM config.weight_assoc wa
+               JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
+               JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) d ON (wa.org_unit = d.id)
+          WHERE active
+          ORDER BY d.distance
+          LIMIT 1;
+    END IF;
+
+    -- No weights? Bad admin! Defaults to handle that anyway.
+    IF weights.id IS NULL THEN
+        weights.user_home_ou    := 5.0;
+        weights.request_ou      := 5.0;
+        weights.pickup_ou       := 5.0;
+        weights.item_owning_ou  := 5.0;
+        weights.item_circ_ou    := 5.0;
+        weights.usr_grp         := 7.0;
+        weights.requestor_grp   := 8.0;
+        weights.circ_modifier   := 4.0;
+        weights.marc_type       := 3.0;
+        weights.marc_form       := 2.0;
+        weights.marc_bib_level  := 1.0;
+        weights.marc_vr_format  := 1.0;
+        weights.juvenile_flag   := 4.0;
+        weights.ref_flag        := 0.0;
+        weights.item_age        := 0.0;
+    END IF;
+
+    -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
+    -- If you break your org tree with funky parenting this may be wrong
+    -- Note: This CTE is duplicated in the find_circ_matrix_matchpoint function, and it may be a good idea to split it off to a function
+    -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
+    WITH all_distance(distance) AS (
+            SELECT depth AS distance FROM actor.org_unit_type
+        UNION
+            SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
+       )
+    SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
+
+    -- To ATTEMPT to make this work like it used to, make it reverse the user/requestor profile ids.
+    -- This may be better implemented as part of the upgrade script?
+    -- Set usr_grp = requestor_grp, requestor_grp = 1 or something when this flag is already set
+    -- Then remove this flag, of course.
+    PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
+
+    IF FOUND THEN
+        -- Note: This, to me, is REALLY hacky. I put it in anyway.
+        -- If you can't tell, this is a single call swap on two variables.
+        SELECT INTO user_object.profile, requestor_object.profile
+                    requestor_object.profile, user_object.profile;
+    END IF;
+
+    -- Select the winning matchpoint into the matchpoint variable for returning
+    SELECT INTO matchpoint m.*
+      FROM  config.hold_matrix_matchpoint m
+            /*LEFT*/ JOIN permission.grp_ancestors_distance( requestor_object.profile ) rpgad ON m.requestor_grp = rpgad.id
+            LEFT JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.usr_grp = upgad.id
+            LEFT JOIN actor.org_unit_ancestors_distance( v_pickup_ou ) puoua ON m.pickup_ou = puoua.id
+            LEFT JOIN actor.org_unit_ancestors_distance( v_request_ou ) rqoua ON m.request_ou = rqoua.id
+            LEFT JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) cnoua ON m.item_owning_ou = cnoua.id
+            LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.item_circ_ou = iooua.id
+            LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou  ) uhoua ON m.user_home_ou = uhoua.id
+      WHERE m.active
+            -- Permission Groups
+         -- AND (m.requestor_grp        IS NULL OR upgad.id IS NOT NULL) -- Optional Requestor Group?
+            AND (m.usr_grp              IS NULL OR upgad.id IS NOT NULL)
+            -- Org Units
+            AND (m.pickup_ou            IS NULL OR (puoua.id IS NOT NULL AND (puoua.distance = 0 OR NOT m.strict_ou_match)))
+            AND (m.request_ou           IS NULL OR (rqoua.id IS NOT NULL AND (rqoua.distance = 0 OR NOT m.strict_ou_match)))
+            AND (m.item_owning_ou       IS NULL OR (cnoua.id IS NOT NULL AND (cnoua.distance = 0 OR NOT m.strict_ou_match)))
+            AND (m.item_circ_ou         IS NULL OR (iooua.id IS NOT NULL AND (iooua.distance = 0 OR NOT m.strict_ou_match)))
+            AND (m.user_home_ou         IS NULL OR (uhoua.id IS NOT NULL AND (uhoua.distance = 0 OR NOT m.strict_ou_match)))
+            -- Static User Checks
+            AND (m.juvenile_flag        IS NULL OR m.juvenile_flag = user_object.juvenile)
+            -- Static Item Checks
+            AND (m.circ_modifier        IS NULL OR m.circ_modifier = item_object.circ_modifier)
+            AND (m.marc_type            IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
+            AND (m.marc_form            IS NULL OR m.marc_form = rec_descriptor.item_form)
+            AND (m.marc_bib_level       IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
+            AND (m.marc_vr_format       IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
+            AND (m.ref_flag             IS NULL OR m.ref_flag = item_object.ref)
+            AND (m.item_age             IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
+      ORDER BY
+            -- Permission Groups
+            CASE WHEN rpgad.distance    IS NOT NULL THEN 2^(2*weights.requestor_grp - (rpgad.distance/denominator)) ELSE 0.0 END +
+            CASE WHEN upgad.distance    IS NOT NULL THEN 2^(2*weights.usr_grp - (upgad.distance/denominator)) ELSE 0.0 END +
+            -- Org Units
+            CASE WHEN puoua.distance    IS NOT NULL THEN 2^(2*weights.pickup_ou - (puoua.distance/denominator)) ELSE 0.0 END +
+            CASE WHEN rqoua.distance    IS NOT NULL THEN 2^(2*weights.request_ou - (rqoua.distance/denominator)) ELSE 0.0 END +
+            CASE WHEN cnoua.distance    IS NOT NULL THEN 2^(2*weights.item_owning_ou - (cnoua.distance/denominator)) ELSE 0.0 END +
+            CASE WHEN iooua.distance    IS NOT NULL THEN 2^(2*weights.item_circ_ou - (iooua.distance/denominator)) ELSE 0.0 END +
+            CASE WHEN uhoua.distance    IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
+            -- Static User Checks       -- Note: 4^x is equiv to 2^(2*x)
+            CASE WHEN m.juvenile_flag   IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
+            -- Static Item Checks
+            CASE WHEN m.circ_modifier   IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
+            CASE WHEN m.marc_type       IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
+            CASE WHEN m.marc_form       IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
+            CASE WHEN m.marc_vr_format  IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
+            CASE WHEN m.ref_flag        IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
+            -- Item age has a slight adjustment to weight based on value.
+            -- This should ensure that a shorter age limit comes first when all else is equal.
+            -- NOTE: This assumes that intervals will normally be in days.
+            CASE WHEN m.item_age            IS NOT NULL THEN 4^weights.item_age - 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
+            -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
+            -- This prevents "we changed the table order by updating a rule, and we started getting different results"
+            m.id;
+
+    -- Return just the ID for now
+    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;
+    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;
+    item_cn_object     asset.call_number%ROWTYPE;
+    item_status_object  config.copy_status%ROWTYPE;
+    item_location_object    asset.copy_location%ROWTYPE;
+    ou_skip              actor.org_unit_setting%ROWTYPE;
+    result            action.matrix_test_result;
+    hold_test        config.hold_matrix_matchpoint%ROWTYPE;
+    use_active_date   TEXT;
+    age_protect_date  TIMESTAMP WITH TIME ZONE;
+    hold_count        INT;
+    hold_transit_prox    INT;
+    frozen_hold_count    INT;
+    context_org_list    INT[];
+    done            BOOL := FALSE;
+    hold_penalty TEXT;
+    v_pickup_ou ALIAS FOR pickup_ou;
+    v_request_ou ALIAS FOR request_ou;
+BEGIN
+    SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+    SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( v_pickup_ou );
+
+    result.success := TRUE;
+
+    -- The HOLD penalty block only applies to new holds.
+    -- The CAPTURE penalty block applies to existing holds.
+    hold_penalty := 'HOLD';
+    IF retargetting THEN
+        hold_penalty := 'CAPTURE';
+    END IF;
+
+    -- 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(v_pickup_ou, v_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;
+
+    SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+    SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status;
+    SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
+
+    -- 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 item_object.holdable IS FALSE THEN
+        result.fail_part := 'item.holdable';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+    END IF;
+
+    IF item_status_object.holdable IS FALSE THEN
+        result.fail_part := 'status.holdable';
+        result.success := FALSE;
+        done := TRUE;
+        RETURN NEXT result;
+    END IF;
+
+    IF item_location_object.holdable IS FALSE THEN
+        result.fail_part := 'location.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 = v_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 unnest(context_org_list) )
+                AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+                AND csp.block_list LIKE '%' || hold_penalty || '%' 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 unnest(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 hold_test.distance_is_from_owner THEN
+            SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
+        ELSE
+            SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
+        END IF;
+        IF use_active_date = 'true' THEN
+            age_protect_date := COALESCE(item_object.active_date, NOW());
+        ELSE
+            age_protect_date := item_object.create_date;
+        END IF;
+        IF age_protect_date + age_protect_object.age > NOW() THEN
+            IF hold_test.distance_is_from_owner THEN
+                SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+                SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = v_pickup_ou;
+            ELSE
+                SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = v_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;
+
+SELECT evergreen.upgrade_deps_block_check('0874', :eg_version);
+
+DROP FUNCTION IF EXISTS evergreen.oils_xpath( TEXT, TEXT, ANYARRAY);
+DROP FUNCTION IF EXISTS public.oils_xpath(TEXT, TEXT, ANYARRAY);
+DROP FUNCTION IF EXISTS public.oils_xpath(TEXT, TEXT);
+DROP FUNCTION IF EXISTS public.oils_xslt_process(TEXT, TEXT);
+
+CREATE OR REPLACE FUNCTION evergreen.xml_famous5_to_text( TEXT ) RETURNS TEXT AS $f$
+ SELECT REPLACE(
+            REPLACE(
+                REPLACE(
+                    REPLACE(
+                        REPLACE( $1, '&lt;', '<'),
+                        '&gt;',
+                        '>'
+                    ),
+                    '&apos;',
+                    $$'$$
+                ), -- ' ... vim
+                '&quot;',
+                '"'
+            ),
+            '&amp;',
+            '&'
+        );
+$f$ LANGUAGE SQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION evergreen.oils_xpath ( TEXT, TEXT, TEXT[] ) RETURNS TEXT[] AS $f$
+    SELECT  ARRAY_AGG(
+                CASE WHEN strpos(x,'<') = 1 THEN -- It's an element node
+                    x
+                ELSE -- it's text-ish
+                    evergreen.xml_famous5_to_text(x)
+                END
+            )
+      FROM  UNNEST(XPATH( $1, $2::XML, $3 )::TEXT[]) x;
+$f$ LANGUAGE SQL IMMUTABLE;
+
+-- Trust me, it's just simpler to duplicate these...
+CREATE OR REPLACE FUNCTION evergreen.oils_xpath ( TEXT, TEXT ) RETURNS TEXT[] AS $f$
+    SELECT  ARRAY_AGG(
+                CASE WHEN strpos(x,'<') = 1 THEN -- It's an element node
+                    x
+                ELSE -- it's text-ish
+                    evergreen.xml_famous5_to_text(x)
+                END
+            )
+      FROM  UNNEST(XPATH( $1, $2::XML)::TEXT[]) x;
+$f$ LANGUAGE SQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION evergreen.oils_xslt_process(TEXT, TEXT) RETURNS TEXT AS $func$
+  use strict;
+
+  use XML::LibXSLT;
+  use XML::LibXML;
+
+  my $doc = shift;
+  my $xslt = shift;
+
+  # The following approach uses the older XML::LibXML 1.69 / XML::LibXSLT 1.68
+  # methods of parsing XML documents and stylesheets, in the hopes of broader
+  # compatibility with distributions
+  my $parser = $_SHARED{'_xslt_process'}{parsers}{xml} || XML::LibXML->new();
+
+  # Cache the XML parser, if we do not already have one
+  $_SHARED{'_xslt_process'}{parsers}{xml} = $parser
+    unless ($_SHARED{'_xslt_process'}{parsers}{xml});
+
+  my $xslt_parser = $_SHARED{'_xslt_process'}{parsers}{xslt} || XML::LibXSLT->new();
+
+  # Cache the XSLT processor, if we do not already have one
+  $_SHARED{'_xslt_process'}{parsers}{xslt} = $xslt_parser
+    unless ($_SHARED{'_xslt_process'}{parsers}{xslt});
+
+  my $stylesheet = $_SHARED{'_xslt_process'}{stylesheets}{$xslt} ||
+    $xslt_parser->parse_stylesheet( $parser->parse_string($xslt) );
+
+  $_SHARED{'_xslt_process'}{stylesheets}{$xslt} = $stylesheet
+    unless ($_SHARED{'_xslt_process'}{stylesheets}{$xslt});
+
+  return $stylesheet->output_string(
+    $stylesheet->transform(
+      $parser->parse_string($doc)
+    )
+  );
+
+$func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
+DECLARE
+    res             authority.simple_heading%ROWTYPE;
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    nfi_used        TEXT;
+    sf              TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    joiner_text     TEXT;
+    sort_text       TEXT;
+    tmp_text        TEXT;
+    tmp_xml         TEXT;
+    first_sf        BOOL;
+    auth_id         INT DEFAULT COALESCE(NULLIF(oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml), ''), '0')::INT; 
+BEGIN
+
+    SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    IF cset IS NULL THEN
+        SELECT  control_set INTO cset
+          FROM  authority.control_set_authority_field
+          WHERE tag IN ( SELECT  UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
+          LIMIT 1;
+    END IF;
+
+    res.record := auth_id;
+
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
+
+        res.atag := acsaf.id;
+        tag_used := acsaf.tag;
+        nfi_used := acsaf.nfi;
+        joiner_text := COALESCE(acsaf.joiner, ' ');
+
+        FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)::TEXT[]) LOOP
+
+            heading_text := COALESCE(
+                oils_xpath_string('./*[contains("'||acsaf.display_sf_list||'",@code)]', tmp_xml, joiner_text),
+                ''
+            );
+
+            IF nfi_used IS NOT NULL THEN
+
+                sort_text := SUBSTRING(
+                    heading_text FROM
+                    COALESCE(
+                        NULLIF(
+                            REGEXP_REPLACE(
+                                oils_xpath_string('./@ind'||nfi_used, tmp_xml::TEXT),
+                                $$\D+$$,
+                                '',
+                                'g'
+                            ),
+                            ''
+                        )::INT,
+                        0
+                    ) + 1
+                );
+
+            ELSE
+                sort_text := heading_text;
+            END IF;
+
+            IF heading_text IS NOT NULL AND heading_text <> '' THEN
+                res.value := heading_text;
+                res.sort_value := public.naco_normalize(sort_text);
+                res.index_vector = to_tsvector('keyword'::regconfig, res.sort_value);
+                RETURN NEXT res;
+            END IF;
+
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION url_verify.extract_urls ( session_id INT, item_id INT ) RETURNS INT AS $$
+DECLARE
+    last_seen_tag TEXT;
+    current_tag TEXT;
+    current_sf TEXT;
+    current_url TEXT;
+    current_ord INT;
+    current_url_pos INT;
+    current_selector url_verify.url_selector%ROWTYPE;
+BEGIN
+    current_ord := 1;
+
+    FOR current_selector IN SELECT * FROM url_verify.url_selector s WHERE s.session = session_id LOOP
+        current_url_pos := 1;
+        LOOP
+            SELECT  (oils_xpath(current_selector.xpath || '/text()', b.marc))[current_url_pos] INTO current_url
+              FROM  biblio.record_entry b
+                    JOIN container.biblio_record_entry_bucket_item c ON (c.target_biblio_record_entry = b.id)
+              WHERE c.id = item_id;
+
+            EXIT WHEN current_url IS NULL;
+
+            SELECT  (oils_xpath(current_selector.xpath || '/../@tag', b.marc))[current_url_pos] INTO current_tag
+              FROM  biblio.record_entry b
+                    JOIN container.biblio_record_entry_bucket_item c ON (c.target_biblio_record_entry = b.id)
+              WHERE c.id = item_id;
+
+            IF current_tag IS NULL THEN
+                current_tag := last_seen_tag;
+            ELSE
+                last_seen_tag := current_tag;
+            END IF;
+
+            SELECT  (oils_xpath(current_selector.xpath || '/@code', b.marc))[current_url_pos] INTO current_sf
+              FROM  biblio.record_entry b
+                    JOIN container.biblio_record_entry_bucket_item c ON (c.target_biblio_record_entry = b.id)
+              WHERE c.id = item_id;
+
+            INSERT INTO url_verify.url (session, item, url_selector, tag, subfield, ord, full_url)
+              VALUES ( session_id, item_id, current_selector.id, current_tag, current_sf, current_ord, current_url);
+
+            current_url_pos := current_url_pos + 1;
+            current_ord := current_ord + 1;
+        END LOOP;
+    END LOOP;
+
+    RETURN current_ord - 1;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.extract_metabib_field_entry ( rid BIGINT, default_joiner TEXT ) RETURNS SETOF metabib.field_entry_template AS $func$
+DECLARE
+    bib     biblio.record_entry%ROWTYPE;
+    idx     config.metabib_field%ROWTYPE;
+    xfrm        config.xml_transform%ROWTYPE;
+    prev_xfrm   TEXT;
+    transformed_xml TEXT;
+    xml_node    TEXT;
+    xml_node_list   TEXT[];
+    facet_text  TEXT;
+    browse_text TEXT;
+    sort_value  TEXT;
+    raw_text    TEXT;
+    curr_text   TEXT;
+    joiner      TEXT := default_joiner; -- XXX will index defs supply a joiner?
+    authority_text TEXT;
+    authority_link BIGINT;
+    output_row  metabib.field_entry_template%ROWTYPE;
+BEGIN
+
+    -- Start out with no field-use bools set
+    output_row.browse_field = FALSE;
+    output_row.facet_field = FALSE;
+    output_row.search_field = FALSE;
+
+    -- Get the record
+    SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
+
+    -- Loop over the indexing entries
+    FOR idx IN SELECT * FROM config.metabib_field ORDER BY format LOOP
+
+        joiner := COALESCE(idx.joiner, default_joiner);
+
+        SELECT INTO xfrm * from config.xml_transform WHERE name = idx.format;
+
+        -- See if we can skip the XSLT ... it's expensive
+        IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+            -- Can't skip the transform
+            IF xfrm.xslt <> '---' THEN
+                transformed_xml := oils_xslt_process(bib.marc,xfrm.xslt);
+            ELSE
+                transformed_xml := bib.marc;
+            END IF;
+
+            prev_xfrm := xfrm.name;
+        END IF;
+
+        xml_node_list := oils_xpath( idx.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+
+        raw_text := NULL;
+        FOR xml_node IN SELECT x FROM unnest(xml_node_list) AS x LOOP
+            CONTINUE WHEN xml_node !~ E'^\\s*<';
+
+            -- XXX much of this should be moved into oils_xpath_string...
+            curr_text := ARRAY_TO_STRING(evergreen.array_remove_item_by_value(evergreen.array_remove_item_by_value(
+                oils_xpath( '//text()', -- get the content of all the nodes within the main selected node
+                    REGEXP_REPLACE( xml_node, E'\\s+', ' ', 'g' ) -- Translate adjacent whitespace to a single space
+                ), ' '), ''),  -- throw away morally empty (bankrupt?) strings
+                joiner
+            );
+
+            CONTINUE WHEN curr_text IS NULL OR curr_text = '';
+
+            IF raw_text IS NOT NULL THEN
+                raw_text := raw_text || joiner;
+            END IF;
+
+            raw_text := COALESCE(raw_text,'') || curr_text;
+
+            -- autosuggest/metabib.browse_entry
+            IF idx.browse_field THEN
+
+                IF idx.browse_xpath IS NOT NULL AND idx.browse_xpath <> '' THEN
+                    browse_text := oils_xpath_string( idx.browse_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    browse_text := curr_text;
+                END IF;
+
+                IF idx.browse_sort_xpath IS NOT NULL AND
+                    idx.browse_sort_xpath <> '' THEN
+
+                    sort_value := oils_xpath_string(
+                        idx.browse_sort_xpath, xml_node, joiner,
+                        ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
+                    );
+                ELSE
+                    sort_value := browse_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(browse_text, E'\\s+', ' ', 'g'));
+                output_row.sort_value :=
+                    public.naco_normalize(sort_value);
+
+                output_row.authority := NULL;
+
+                IF idx.authority_xpath IS NOT NULL AND idx.authority_xpath <> '' THEN
+                    authority_text := oils_xpath_string(
+                        idx.authority_xpath, xml_node, joiner,
+                        ARRAY[
+                            ARRAY[xfrm.prefix, xfrm.namespace_uri],
+                            ARRAY['xlink','http://www.w3.org/1999/xlink']
+                        ]
+                    );
+
+                    IF authority_text ~ '^\d+$' THEN
+                        authority_link := authority_text::BIGINT;
+                        PERFORM * FROM authority.record_entry WHERE id = authority_link;
+                        IF FOUND THEN
+                            output_row.authority := authority_link;
+                        END IF;
+                    END IF;
+
+                END IF;
+
+                output_row.browse_field = TRUE;
+                -- Returning browse rows with search_field = true for search+browse
+                -- configs allows us to retain granularity of being able to search
+                -- browse fields with "starts with" type operators (for example, for
+                -- titles of songs in music albums)
+                IF idx.search_field THEN
+                    output_row.search_field = TRUE;
+                END IF;
+                RETURN NEXT output_row;
+                output_row.browse_field = FALSE;
+                output_row.search_field = FALSE;
+                output_row.sort_value := NULL;
+            END IF;
+
+            -- insert raw node text for faceting
+            IF idx.facet_field THEN
+
+                IF idx.facet_xpath IS NOT NULL AND idx.facet_xpath <> '' THEN
+                    facet_text := oils_xpath_string( idx.facet_xpath, xml_node, joiner, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]] );
+                ELSE
+                    facet_text := curr_text;
+                END IF;
+
+                output_row.field_class = idx.field_class;
+                output_row.field = -1 * idx.id;
+                output_row.source = rid;
+                output_row.value = BTRIM(REGEXP_REPLACE(facet_text, E'\\s+', ' ', 'g'));
+
+                output_row.facet_field = TRUE;
+                RETURN NEXT output_row;
+                output_row.facet_field = FALSE;
+            END IF;
+
+        END LOOP;
+
+        CONTINUE WHEN raw_text IS NULL OR raw_text = '';
+
+        -- insert combined node text for searching
+        IF idx.search_field THEN
+            output_row.field_class = idx.field_class;
+            output_row.field = idx.id;
+            output_row.source = rid;
+            output_row.value = BTRIM(REGEXP_REPLACE(raw_text, E'\\s+', ' ', 'g'));
+
+            output_row.search_field = TRUE;
+            RETURN NEXT output_row;
+            output_row.search_field = FALSE;
+        END IF;
+
+    END LOOP;
+
+END;
+
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION metabib.reingest_record_attributes (rid BIGINT, pattr_list TEXT[] DEFAULT NULL, prmarc TEXT DEFAULT NULL, rdeleted BOOL DEFAULT TRUE) RETURNS VOID AS $func$
+DECLARE
+    transformed_xml TEXT;
+    rmarc           TEXT := prmarc;
+    tmp_val         TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_vector     INT[] := '{}'::INT[];
+    attr_vector_tmp INT[];
+    attr_list       TEXT[] := pattr_list;
+    attr_value      TEXT[];
+    norm_attr_value TEXT[];
+    tmp_xml         TEXT;
+    attr_def        config.record_attr_definition%ROWTYPE;
+    ccvm_row        config.coded_value_map%ROWTYPE;
+BEGIN
+
+    IF attr_list IS NULL OR rdeleted THEN -- need to do the full dance on INSERT or undelete
+        SELECT ARRAY_AGG(name) INTO attr_list FROM config.record_attr_definition;
+    END IF;
+
+    IF rmarc IS NULL THEN
+        SELECT marc INTO rmarc FROM biblio.record_entry WHERE id = rid;
+    END IF;
+
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE NOT composite AND name = ANY( attr_list ) ORDER BY format LOOP
+
+        attr_value := '{}'::TEXT[];
+        norm_attr_value := '{}'::TEXT[];
+        attr_vector_tmp := '{}'::INT[];
+
+        SELECT * INTO ccvm_row FROM config.coded_value_map c WHERE c.ctype = attr_def.name LIMIT 1; 
+
+        -- tag+sf attrs only support SVF
+        IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+            SELECT  ARRAY[ARRAY_TO_STRING(ARRAY_AGG(value), COALESCE(attr_def.joiner,' '))] INTO attr_value
+              FROM  (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
+              WHERE record = rid
+                    AND tag LIKE attr_def.tag
+                    AND CASE
+                        WHEN attr_def.sf_list IS NOT NULL 
+                            THEN POSITION(subfield IN attr_def.sf_list) > 0
+                        ELSE TRUE
+                    END
+              GROUP BY tag
+              ORDER BY tag
+              LIMIT 1;
+
+        ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+            attr_value := vandelay.marc21_extract_fixed_field_list(rmarc, attr_def.fixed_field);
+
+            IF NOT attr_def.multi THEN
+                attr_value := ARRAY[attr_value[1]];
+            END IF;
+
+        ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+            SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+        
+            -- See if we can skip the XSLT ... it's expensive
+            IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                -- Can't skip the transform
+                IF xfrm.xslt <> '---' THEN
+                    transformed_xml := oils_xslt_process(rmarc,xfrm.xslt);
+                ELSE
+                    transformed_xml := rmarc;
+                END IF;
+    
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            IF xfrm.name IS NULL THEN
+                -- just grab the marcxml (empty) transform
+                SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                prev_xfrm := xfrm.name;
+            END IF;
+
+            FOR tmp_xml IN SELECT oils_xpath(attr_def.xpath, transformed_xml, ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]) LOOP
+                tmp_val := oils_xpath_string(
+                                '//*',
+                                tmp_xml,
+                                COALESCE(attr_def.joiner,' '),
+                                ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]
+                            );
+                IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
+                    attr_value := attr_value || tmp_val;
+                    EXIT WHEN NOT attr_def.multi;
+                END IF;
+            END LOOP;
+
+        ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+            SELECT  ARRAY_AGG(m.value) INTO attr_value
+              FROM  vandelay.marc21_physical_characteristics(rmarc) v
+                    LEFT JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
+              WHERE v.subfield = attr_def.phys_char_sf AND (m.value IS NOT NULL AND BTRIM(m.value) <> '')
+                    AND ( ccvm_row.id IS NULL OR ( ccvm_row.id IS NOT NULL AND v.id IS NOT NULL) );
+
+            IF NOT attr_def.multi THEN
+                attr_value := ARRAY[attr_value[1]];
+            END IF;
+
+        END IF;
+
+                -- apply index normalizers to attr_value
+        FOR tmp_val IN SELECT value FROM UNNEST(attr_value) x(value) LOOP
+            FOR normalizer IN
+                SELECT  n.func AS func,
+                        n.param_count AS param_count,
+                        m.params AS params
+                  FROM  config.index_normalizer n
+                        JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+                  WHERE attr = attr_def.name
+                  ORDER BY m.pos LOOP
+                    EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                    COALESCE( quote_literal( tmp_val ), 'NULL' ) ||
+                        CASE
+                            WHEN normalizer.param_count > 0
+                                THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                                ELSE ''
+                            END ||
+                    ')' INTO tmp_val;
+
+            END LOOP;
+            IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
+                norm_attr_value := norm_attr_value || tmp_val;
+            END IF;
+        END LOOP;
+        
+        IF attr_def.filter THEN
+            -- Create unknown uncontrolled values and find the IDs of the values
+            IF ccvm_row.id IS NULL THEN
+                FOR tmp_val IN SELECT value FROM UNNEST(norm_attr_value) x(value) LOOP
+                    IF tmp_val IS NOT NULL AND BTRIM(tmp_val) <> '' THEN
+                        BEGIN -- use subtransaction to isolate unique constraint violations
+                            INSERT INTO metabib.uncontrolled_record_attr_value ( attr, value ) VALUES ( attr_def.name, tmp_val );
+                        EXCEPTION WHEN unique_violation THEN END;
+                    END IF;
+                END LOOP;
+
+                SELECT ARRAY_AGG(id) INTO attr_vector_tmp FROM metabib.uncontrolled_record_attr_value WHERE attr = attr_def.name AND value = ANY( norm_attr_value );
+            ELSE
+                SELECT ARRAY_AGG(id) INTO attr_vector_tmp FROM config.coded_value_map WHERE ctype = attr_def.name AND code = ANY( norm_attr_value );
+            END IF;
+
+            -- Add the new value to the vector
+            attr_vector := attr_vector || attr_vector_tmp;
+        END IF;
+
+        IF attr_def.sorter AND norm_attr_value[1] IS NOT NULL THEN
+            DELETE FROM metabib.record_sorter WHERE source = rid AND attr = attr_def.name;
+            INSERT INTO metabib.record_sorter (source, attr, value) VALUES (rid, attr_def.name, norm_attr_value[1]);
+        END IF;
+
+    END LOOP;
+
+/* We may need to rewrite the vlist to contain
+   the intersection of new values for requested
+   attrs and old values for ignored attrs. To
+   do this, we take the old attr vlist and
+   subtract any values that are valid for the
+   requested attrs, and then add back the new
+   set of attr values. */
+
+    IF ARRAY_LENGTH(pattr_list, 1) > 0 THEN 
+        SELECT vlist INTO attr_vector_tmp FROM metabib.record_attr_vector_list WHERE source = rid;
+        SELECT attr_vector_tmp - ARRAY_AGG(id::INT) INTO attr_vector_tmp FROM metabib.full_attr_id_map WHERE attr = ANY (pattr_list);
+        attr_vector := attr_vector || attr_vector_tmp;
+    END IF;
+
+    -- On to composite attributes, now that the record attrs have been pulled.  Processed in name order, so later composite
+    -- attributes can depend on earlier ones.
+    PERFORM metabib.compile_composite_attr_cache_init();
+    FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE composite AND name = ANY( attr_list ) ORDER BY name LOOP
+
+        FOR ccvm_row IN SELECT * FROM config.coded_value_map c WHERE c.ctype = attr_def.name ORDER BY value LOOP
+
+            tmp_val := metabib.compile_composite_attr( ccvm_row.id );
+            CONTINUE WHEN tmp_val IS NULL OR tmp_val = ''; -- nothing to do
+
+            IF attr_def.filter THEN
+                IF attr_vector @@ tmp_val::query_int THEN
+                    attr_vector = attr_vector + intset(ccvm_row.id);
+                    EXIT WHEN NOT attr_def.multi;
+                END IF;
+            END IF;
+
+            IF attr_def.sorter THEN
+                IF attr_vector @@ tmp_val THEN
+                    DELETE FROM metabib.record_sorter WHERE source = rid AND attr = attr_def.name;
+                    INSERT INTO metabib.record_sorter (source, attr, value) VALUES (rid, attr_def.name, ccvm_row.code);
+                END IF;
+            END IF;
+
+        END LOOP;
+
+    END LOOP;
+
+    IF ARRAY_LENGTH(attr_vector, 1) > 0 THEN
+        IF rdeleted THEN -- initial insert OR revivication
+            DELETE FROM metabib.record_attr_vector_list WHERE source = rid;
+            INSERT INTO metabib.record_attr_vector_list (source, vlist) VALUES (rid, attr_vector);
+        ELSE
+            UPDATE metabib.record_attr_vector_list SET vlist = attr_vector WHERE source = rid;
+        END IF;
+    END IF;
+
+END;
+
+$func$ LANGUAGE PLPGSQL;
+
+COMMIT;
+
+-- END OF UPGRADE SCRIPT 1
+
+-- START OF UPGRADE SCRIPT 2
+ALTER TABLE authority.record_entry DISABLE TRIGGER a_marcxml_is_well_formed;
+ALTER TABLE authority.record_entry DISABLE TRIGGER aaa_auth_ingest_or_delete;
+ALTER TABLE authority.record_entry DISABLE TRIGGER b_maintain_901;
+ALTER TABLE authority.record_entry DISABLE TRIGGER c_maintain_control_numbers;
+ALTER TABLE authority.record_entry DISABLE TRIGGER map_thesaurus_to_control_set;
+
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0875', :eg_version);
+
+ALTER TABLE authority.record_entry ADD COLUMN heading TEXT, ADD COLUMN simple_heading TEXT;
+
+DROP INDEX IF EXISTS authority.unique_by_heading_and_thesaurus;
+DROP INDEX IF EXISTS authority.by_heading_and_thesaurus;
+DROP INDEX IF EXISTS authority.by_heading;
+
+-- Update without indexes for HOT update
+UPDATE  authority.record_entry
+  SET   heading = authority.normalize_heading( marc ),
+        simple_heading = authority.simple_normalize_heading( marc );
+
+CREATE INDEX by_heading_and_thesaurus ON authority.record_entry (heading) WHERE deleted IS FALSE or deleted = FALSE;
+CREATE INDEX by_heading ON authority.record_entry (simple_heading) WHERE deleted IS FALSE or deleted = FALSE;
+
+-- Add the trigger
+CREATE OR REPLACE FUNCTION authority.normalize_heading_for_upsert () RETURNS TRIGGER AS $f$
+BEGIN
+    NEW.heading := authority.normalize_heading( NEW.marc );
+    NEW.simple_heading := authority.simple_normalize_heading( NEW.marc );
+    RETURN NEW;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER update_headings_tgr BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.normalize_heading_for_upsert();
+
+ALTER FUNCTION authority.normalize_heading(TEXT, BOOL) STABLE STRICT;
+ALTER FUNCTION authority.normalize_heading(TEXT) STABLE STRICT;
+ALTER FUNCTION authority.simple_normalize_heading(TEXT) STABLE STRICT;
+ALTER FUNCTION authority.simple_heading_set(TEXT) STABLE STRICT;
+
+COMMIT;
+
+ALTER TABLE authority.record_entry ENABLE TRIGGER a_marcxml_is_well_formed;
+ALTER TABLE authority.record_entry ENABLE TRIGGER aaa_auth_ingest_or_delete;
+ALTER TABLE authority.record_entry ENABLE TRIGGER b_maintain_901;
+ALTER TABLE authority.record_entry ENABLE TRIGGER c_maintain_control_numbers;
+ALTER TABLE authority.record_entry ENABLE TRIGGER map_thesaurus_to_control_set;
+
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0876', :eg_version);
+
+INSERT INTO permission.perm_list ( code, description ) VALUES
+ ( 'group_application.user.staff.admin.system_admin', oils_i18n_gettext( '',
+    'Allow a user to add/remove users to/from the "System Administrator" group', 'ppl', 'description' )),
+ ( 'group_application.user.staff.cat_admin', oils_i18n_gettext( '', 
+    'Allow a user to add/remove users to/from the "Cataloging Administrator" group', 'ppl', 'description' )),
+ ( 'group_application.user.staff.circ_admin', oils_i18n_gettext( '', 
+    'Allow a user to add/remove users to/from the "Circulation Administrator" group', 'ppl', 'description' )),
+ ( 'group_application.user.staff.data_review', oils_i18n_gettext( '', 
+    'Allow a user to add/remove users to/from the "Data Review" group', 'ppl', 'description' )),
+ ( 'group_application.user.staff.volunteers', oils_i18n_gettext( '', 
+    'Allow a user to add/remove users to/from the "Volunteers" group', 'ppl', 'description' ))
+;
+
+SELECT evergreen.upgrade_deps_block_check('0877', :eg_version);
+
+-- Don't use Series search field as the browse field
+UPDATE config.metabib_field SET
+       browse_field = FALSE,
+       browse_xpath = NULL,
+       browse_sort_xpath = NULL,
+       xpath = $$//mods32:mods/mods32:relatedItem[@type="series"]/mods32:titleInfo[not(@type="nfi")]$$
+WHERE id = 1;
+
+-- Create a new series browse config
+INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, authority_xpath, browse_field, browse_sort_xpath ) VALUES
+    (32, 'series', 'browse', oils_i18n_gettext(32, 'Series Title (Browse)', 'cmf', 'label'), 'mods32', $$//mods32:mods/mods32:relatedItem[@type="series"]/mods32:titleInfo[@type="nfi"]$$, FALSE, '//@xlink:href', TRUE, $$*[local-name() != "nonSort"]$$ );
+
+SELECT evergreen.upgrade_deps_block_check('0878', :eg_version);
+
+CREATE OR REPLACE FUNCTION metabib.reingest_metabib_field_entries( bib_id BIGINT, skip_facet BOOL DEFAULT FALSE, skip_browse BOOL DEFAULT FALSE, skip_search BOOL DEFAULT FALSE ) RETURNS VOID AS $func$
+DECLARE
+    fclass          RECORD;
+    ind_data        metabib.field_entry_template%ROWTYPE;
+    mbe_row         metabib.browse_entry%ROWTYPE;
+    mbe_id          BIGINT;
+    b_skip_facet    BOOL;
+    b_skip_browse   BOOL;
+    b_skip_search   BOOL;
+    value_prepped   TEXT;
+BEGIN
+
+    SELECT COALESCE(NULLIF(skip_facet, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_facet_indexing' AND enabled)) INTO b_skip_facet;
+    SELECT COALESCE(NULLIF(skip_browse, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_browse_indexing' AND enabled)) INTO b_skip_browse;
+    SELECT COALESCE(NULLIF(skip_search, FALSE), EXISTS (SELECT enabled FROM config.internal_flag WHERE name =  'ingest.skip_search_indexing' AND enabled)) INTO b_skip_search;
+
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        IF NOT b_skip_search 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;
+        END IF;
+        IF NOT b_skip_facet THEN
+            DELETE FROM metabib.facet_entry WHERE source = bib_id;
+        END IF;
+        IF NOT b_skip_browse THEN
+            DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
+        END IF;
+    END IF;
+
+    FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
+
+       -- don't store what has been normalized away
+        CONTINUE WHEN ind_data.value IS NULL;
+
+        IF ind_data.field < 0 THEN
+            ind_data.field = -1 * ind_data.field;
+        END IF;
+
+        IF ind_data.facet_field AND NOT b_skip_facet THEN
+            INSERT INTO metabib.facet_entry (field, source, value)
+                VALUES (ind_data.field, ind_data.source, ind_data.value);
+        END IF;
+
+        IF ind_data.browse_field AND NOT b_skip_browse THEN
+            -- A caveat about this SELECT: this should take care of replacing
+            -- old mbe rows when data changes, but not if normalization (by
+            -- which I mean specifically the output of
+            -- evergreen.oils_tsearch2()) changes.  It may or may not be
+            -- expensive to add a comparison of index_vector to index_vector
+            -- to the WHERE clause below.
+
+            CONTINUE WHEN ind_data.sort_value IS NULL;
+
+            value_prepped := metabib.browse_normalize(ind_data.value, ind_data.field);
+            SELECT INTO mbe_row * FROM metabib.browse_entry
+                WHERE value = value_prepped AND sort_value = ind_data.sort_value;
+
+            IF FOUND THEN
+                mbe_id := mbe_row.id;
+            ELSE
+                INSERT INTO metabib.browse_entry
+                    ( value, sort_value ) VALUES
+                    ( value_prepped, ind_data.sort_value );
+
+                mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
+            END IF;
+
+            INSERT INTO metabib.browse_entry_def_map (entry, def, source, authority)
+                VALUES (mbe_id, ind_data.field, ind_data.source, ind_data.authority);
+        END IF;
+
+        IF ind_data.search_field AND NOT b_skip_search THEN
+            -- Avoid inserting duplicate rows
+            EXECUTE 'SELECT 1 FROM metabib.' || ind_data.field_class ||
+                '_field_entry WHERE field = $1 AND source = $2 AND value = $3'
+                INTO mbe_id USING ind_data.field, ind_data.source, ind_data.value;
+                -- RAISE NOTICE 'Search for an already matching row returned %', mbe_id;
+            IF mbe_id IS NULL THEN
+                EXECUTE $$
+                INSERT INTO metabib.$$ || ind_data.field_class || $$_field_entry (field, source, value)
+                    VALUES ($$ ||
+                        quote_literal(ind_data.field) || $$, $$ ||
+                        quote_literal(ind_data.source) || $$, $$ ||
+                        quote_literal(ind_data.value) ||
+                    $$);$$;
+            END IF;
+        END IF;
+
+    END LOOP;
+
+    IF NOT b_skip_search THEN
+        PERFORM metabib.update_combined_index_vectors(bib_id);
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0879', :eg_version);
+
+CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
+    node vandelay.match_set_point,
+    tags_rstore HSTORE
+) RETURNS VOID AS $$
+DECLARE
+    jrow        TEXT;
+    my_alias    TEXT;
+    op          TEXT;
+    tagkey      TEXT;
+    caseless    BOOL;
+    jrow_count  INT;
+    my_using    TEXT;
+    my_join     TEXT;
+BEGIN
+    -- remember $1 is tags_rstore, and $2 is svf_rstore
+
+    caseless := FALSE;
+    SELECT COUNT(*) INTO jrow_count FROM _vandelay_tmp_jrows;
+    IF jrow_count > 0 THEN
+        my_using := ' USING (record)';
+        my_join := 'FULL OUTER JOIN';
+    ELSE
+        my_using := '';
+        my_join := 'FROM';
+    END IF;
+
+    IF node.tag IS NOT NULL THEN
+        caseless := (node.tag IN ('020', '022', '024'));
+        tagkey := node.tag;
+        IF node.subfield IS NOT NULL THEN
+            tagkey := tagkey || node.subfield;
+        END IF;
+    END IF;
+
+    IF node.negate THEN
+        IF caseless THEN
+            op := 'NOT LIKE';
+        ELSE
+            op := '<>';
+        END IF;
+    ELSE
+        IF caseless THEN
+            op := 'LIKE';
+        ELSE
+            op := '=';
+        END IF;
+    END IF;
+
+    my_alias := 'n' || node.id::TEXT;
+
+    jrow := my_join || ' (SELECT *, ';
+    IF node.tag IS NOT NULL THEN
+        jrow := jrow  || node.quality ||
+            ' AS quality FROM metabib.full_rec mfr WHERE mfr.tag = ''' ||
+            node.tag || '''';
+        IF node.subfield IS NOT NULL THEN
+            jrow := jrow || ' AND mfr.subfield = ''' ||
+                node.subfield || '''';
+        END IF;
+        jrow := jrow || ' AND (';
+        jrow := jrow || vandelay._node_tag_comparisons(caseless, op, tags_rstore, tagkey);
+        jrow := jrow || ')) ' || my_alias || my_using || E'\n';
+    ELSE    -- svf
+        jrow := jrow || 'id AS record, ' || node.quality ||
+            ' AS quality FROM metabib.record_attr_flat mraf WHERE mraf.attr = ''' ||
+            node.svf || ''' AND mraf.value ' || op || ' $2->''' || node.svf || ''') ' ||
+            my_alias || my_using || E'\n';
+    END IF;
+    INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
+END;
+$$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0880', :eg_version);
+
+CREATE OR REPLACE FUNCTION authority.calculate_authority_linking(
+    rec_id BIGINT, rec_control_set INT, rec_marc_xml XML
+) RETURNS SETOF authority.authority_linking AS $func$
+DECLARE
+    acsaf       authority.control_set_authority_field%ROWTYPE;
+    link        TEXT;
+    aal         authority.authority_linking%ROWTYPE;
+BEGIN
+    IF rec_control_set IS NULL THEN
+        -- No control_set on record?  Guess at one
+        SELECT control_set INTO rec_control_set
+            FROM authority.control_set_authority_field
+            WHERE tag IN (
+                SELECT UNNEST(
+                    XPATH('//*[starts-with(@tag,"1")]/@tag',rec_marc_xml)::TEXT[]
+                )
+            ) LIMIT 1;
+
+        IF NOT FOUND THEN
+            RAISE WARNING 'Could not even guess at control set for authority record %', rec_id;
+            RETURN;
+        END IF;
+    END IF;
+
+    aal.source := rec_id;
+
+    FOR acsaf IN
+        SELECT * FROM authority.control_set_authority_field
+        WHERE control_set = rec_control_set
+            AND linking_subfield IS NOT NULL
+            AND main_entry IS NOT NULL
+    LOOP
+        -- Loop over the trailing-number contents of all linking subfields
+        FOR link IN
+            SELECT  SUBSTRING( x::TEXT, '\d+$' )
+              FROM  UNNEST(
+                        XPATH(
+                            '//*[@tag="'
+                                || acsaf.tag
+                                || '"]/*[@code="'
+                                || acsaf.linking_subfield
+                                || '"]/text()',
+                            rec_marc_xml
+                        )
+                    ) x
+        LOOP
+
+            -- Ignore links that are null, malformed, circular, or point to
+            -- non-existent authority records.
+            IF link IS NOT NULL AND link::BIGINT <> rec_id THEN
+                PERFORM * FROM authority.record_entry WHERE id = link::BIGINT;
+                IF FOUND THEN
+                    aal.target := link::BIGINT;
+                    aal.field := acsaf.id;
+                    RETURN NEXT aal;
+                END IF;
+            END IF;
+        END LOOP;
+    END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0881', :eg_version);
+
+UPDATE config.org_unit_setting_type
+    SET description = replace(replace(description,'Original','Physical'),'"ol"','"physical_loc"')
+    WHERE name = 'opac.org_unit_hiding.depth';
+
+SELECT evergreen.upgrade_deps_block_check('0882', :eg_version);
+
+CREATE OR REPLACE FUNCTION search.query_parser_fts (
+
+    param_search_ou INT,
+    param_depth     INT,
+    param_query     TEXT,
+    param_statuses  INT[],
+    param_locations INT[],
+    param_offset    INT,
+    param_check     INT,
+    param_limit     INT,
+    metarecord      BOOL,
+    staff           BOOL,
+    deleted_search  BOOL,
+    param_pref_ou   INT DEFAULT NULL
+) RETURNS SETOF search.search_result AS $func$
+DECLARE
+
+    current_res         search.search_result%ROWTYPE;
+    search_org_list     INT[];
+    luri_org_list       INT[];
+    tmp_int_list        INT[];
+
+    check_limit         INT;
+    core_limit          INT;
+    core_offset         INT;
+    tmp_int             INT;
+
+    core_result         RECORD;
+    core_cursor         REFCURSOR;
+    core_rel_query      TEXT;
+
+    total_count         INT := 0;
+    check_count         INT := 0;
+    deleted_count       INT := 0;
+    visible_count       INT := 0;
+    excluded_count      INT := 0;
+
+    luri_as_copy        BOOL;
+BEGIN
+
+    check_limit := COALESCE( param_check, 1000 );
+    core_limit  := COALESCE( param_limit, 25000 );
+    core_offset := COALESCE( param_offset, 0 );
+
+    SELECT COALESCE( enabled, FALSE ) INTO luri_as_copy FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy';
+
+    -- core_skip_chk := COALESCE( param_skip_chk, 1 );
+
+    IF param_search_ou > 0 THEN
+        IF param_depth IS NOT NULL THEN
+            SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
+        ELSE
+            SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
+        END IF;
+
+        IF luri_as_copy THEN
+            SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_full_path( param_search_ou );
+        ELSE
+            SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
+        END IF;
+
+    ELSIF param_search_ou < 0 THEN
+        SELECT ARRAY_AGG(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
+
+        FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
+
+            IF luri_as_copy THEN
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( tmp_int );
+            ELSE
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
+            END IF;
+
+            luri_org_list := luri_org_list || tmp_int_list;
+        END LOOP;
+
+        SELECT ARRAY_AGG(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
+
+    ELSIF param_search_ou = 0 THEN
+        -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
+    END IF;
+
+    IF param_pref_ou IS NOT NULL THEN
+            IF luri_as_copy THEN
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( param_pref_ou );
+            ELSE
+                SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( param_pref_ou );
+            END IF;
+
+        luri_org_list := luri_org_list || tmp_int_list;
+    END IF;
+
+    OPEN core_cursor FOR EXECUTE param_query;
+
+    LOOP
+
+        FETCH core_cursor INTO core_result;
+        EXIT WHEN NOT FOUND;
+        EXIT WHEN total_count >= core_limit;
+
+        total_count := total_count + 1;
+
+        CONTINUE WHEN total_count NOT BETWEEN  core_offset + 1 AND check_limit + core_offset;
+
+        check_count := check_count + 1;
+
+        IF NOT deleted_search THEN
+
+            PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+            IF NOT FOUND THEN
+                -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
+                deleted_count := deleted_count + 1;
+                CONTINUE;
+            END IF;
+
+            PERFORM 1
+              FROM  biblio.record_entry b
+                    JOIN config.bib_source s ON (b.source = s.id)
+              WHERE s.transcendant
+                    AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+
+            IF FOUND THEN
+                -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
+                visible_count := visible_count + 1;
+
+                current_res.id = core_result.id;
+                current_res.rel = core_result.rel;
+
+                tmp_int := 1;
+                IF metarecord THEN
+                    SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+                END IF;
+
+                IF tmp_int = 1 THEN
+                    current_res.record = core_result.records[1];
+                ELSE
+                    current_res.record = NULL;
+                END IF;
+
+                RETURN NEXT current_res;
+
+                CONTINUE;
+            END IF;
+
+            PERFORM 1
+              FROM  asset.call_number cn
+                    JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
+                    JOIN asset.uri uri ON (map.uri = uri.id)
+              WHERE NOT cn.deleted
+                    AND cn.label = '##URI##'
+                    AND uri.active
+                    AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
+                    AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                    AND cn.owning_lib IN ( SELECT * FROM unnest( luri_org_list ) )
+              LIMIT 1;
+
+            IF FOUND THEN
+                -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
+                visible_count := visible_count + 1;
+
+                current_res.id = core_result.id;
+                current_res.rel = core_result.rel;
+
+                tmp_int := 1;
+                IF metarecord THEN
+                    SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+                END IF;
+
+                IF tmp_int = 1 THEN
+                    current_res.record = core_result.records[1];
+                ELSE
+                    current_res.record = NULL;
+                END IF;
+
+                RETURN NEXT current_res;
+
+                CONTINUE;
+            END IF;
+
+            IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+                    -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            END IF;
+
+            IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+                        -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            END IF;
+
+            IF staff IS NULL OR NOT staff THEN
+
+                PERFORM 1
+                  FROM  asset.opac_visible_copies
+                  WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy)
+                      WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+
+                        -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            ELSE
+
+                PERFORM 1
+                  FROM  asset.call_number cn
+                        JOIN asset.copy cp ON (cp.call_number = cn.id)
+                  WHERE NOT cn.deleted
+                        AND NOT cp.deleted
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+
+                    PERFORM 1
+                      FROM  biblio.peer_bib_copy_map pr
+                            JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                      WHERE NOT cp.deleted
+                            AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                            AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                      LIMIT 1;
+
+                    IF NOT FOUND THEN
+
+                        PERFORM 1
+                          FROM  asset.call_number cn
+                                JOIN asset.copy cp ON (cp.call_number = cn.id)
+                          WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                                AND NOT cp.deleted
+                          LIMIT 1;
+
+                        IF NOT FOUND THEN
+                            -- Recheck Located URI visibility in the case of no "foreign" copies
+                            PERFORM 1
+                              FROM  asset.call_number cn
+                                    JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
+                                    JOIN asset.uri uri ON (map.uri = uri.id)
+                              WHERE NOT cn.deleted
+                                    AND cn.label = '##URI##'
+                                    AND uri.active
+                                    AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                                    AND cn.owning_lib NOT IN ( SELECT * FROM unnest( luri_org_list ) )
+                              LIMIT 1;
+
+                            IF FOUND THEN
+                                -- RAISE NOTICE ' % were excluded for foreign located URIs... ', core_result.records;
+                                excluded_count := excluded_count + 1;
+                                CONTINUE;
+                            END IF;
+                        ELSE
+                            -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                            excluded_count := excluded_count + 1;
+                            CONTINUE;
+                        END IF;
+                    END IF;
+
+                END IF;
+
+            END IF;
+
+        END IF;
+
+        visible_count := visible_count + 1;
+
+        current_res.id = core_result.id;
+        current_res.rel = core_result.rel;
+
+        tmp_int := 1;
+        IF metarecord THEN
+            SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+        END IF;
+
+        IF tmp_int = 1 THEN
+            current_res.record = core_result.records[1];
+        ELSE
+            current_res.record = NULL;
+        END IF;
+
+        RETURN NEXT current_res;
+
+        IF visible_count % 1000 = 0 THEN
+            -- RAISE NOTICE ' % visible so far ... ', visible_count;
+        END IF;
+
+    END LOOP;
+
+    current_res.id = NULL;
+    current_res.rel = NULL;
+    current_res.record = NULL;
+    current_res.total = total_count;
+    current_res.checked = check_count;
+    current_res.deleted = deleted_count;
+    current_res.visible = visible_count;
+    current_res.excluded = excluded_count;
+
+    CLOSE core_cursor;
+
+    RETURN NEXT current_res;
+
+END;
+$func$ LANGUAGE PLPGSQL;
+
+SELECT evergreen.upgrade_deps_block_check('0883', :eg_version);
+
+-- This is a placeholder for 0883 which will be a backported version of the
+-- staff URI visibility function for rel_2_5. This script does nothing for
+-- rel_2_6 and later.
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0884', :eg_version);
+
+UPDATE container.biblio_record_entry_bucket_type
+SET label = oils_i18n_gettext(
+       'bookbag',
+       'Book List',
+       'cbrebt',
+       'label'
+) WHERE code = 'bookbag';
+
+UPDATE container.user_bucket_type
+SET label = oils_i18n_gettext(
+       'folks:pub_book_bags.view',
+       'List Published Book Lists',
+       'cubt',
+       'label'
+) WHERE code = 'folks:pub_book_bags.view';
+
+UPDATE container.user_bucket_type
+SET label = oils_i18n_gettext(
+       'folks:pub_book_bags.add',
+       'Add to Published Book Lists',
+       'cubt',
+       'label'
+) WHERE code = 'folks:pub_book_bags.add';
+
+UPDATE action_trigger.hook
+SET description = oils_i18n_gettext(
+       'container.biblio_record_entry_bucket.csv',
+       'Produce a CSV file representing a book list',
+       'ath',
+       'description'
+) WHERE key = 'container.biblio_record_entry_bucket.csv';
+
+UPDATE action_trigger.reactor
+SET description = oils_i18n_gettext(
+       'ContainerCSV',
+       'Facilitates producing a CSV file representing a book list by introducing an "items" variable into the TT environment, sorted as dictated according to user params',
+       'atr',
+       'description'
+)
+WHERE module = 'ContainerCSV';
+
+UPDATE action_trigger.event_definition
+SET template = REPLACE(template, 'bookbag', 'book list'),
+name = 'Book List CSV'
+WHERE name = 'Bookbag CSV';
+
+UPDATE config.org_unit_setting_type
+SET description = oils_i18n_gettext(
+       'opac.patron.temporary_list_warn',
+       'Present a warning dialog to the patron when a patron adds a book to a temporary book list.',
+       'coust',
+       'description'
+) WHERE name = 'opac.patron.temporary_list_warn';
+
+UPDATE config.usr_setting_type
+SET label = oils_i18n_gettext(
+       'opac.default_list',
+       'Default list to use when adding to a list',
+       'cust',
+       'label'
+),
+description = oils_i18n_gettext(
+       'opac.default_list',
+       'Default list to use when adding to a list',
+       'cust',
+       'description'
+) WHERE name = 'opac.default_list';
+
+SELECT evergreen.upgrade_deps_block_check('0885', :eg_version);
+
+CREATE OR REPLACE FUNCTION evergreen.ranked_volumes(
+    bibid BIGINT[],
+    ouid INT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    pref_lib INT DEFAULT NULL,
+    includes TEXT[] DEFAULT NULL::TEXT[]
+) RETURNS TABLE(id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT) AS $$
+    WITH RECURSIVE ou_depth AS (
+        SELECT COALESCE(
+            $3,
+            (
+                SELECT depth
+                FROM actor.org_unit_type aout
+                    INNER JOIN actor.org_unit ou ON ou_type = aout.id
+                WHERE ou.id = $2
+            )
+        ) AS depth
+    ), descendant_depth AS (
+        SELECT  ou.id,
+                ou.parent_ou,
+                out.depth
+        FROM  actor.org_unit ou
+                JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+                JOIN anscestor_depth ad ON (ad.id = ou.id),
+                ou_depth
+        WHERE ad.depth = ou_depth.depth
+            UNION ALL
+        SELECT  ou.id,
+                ou.parent_ou,
+                out.depth
+        FROM  actor.org_unit ou
+                JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+                JOIN descendant_depth ot ON (ot.id = ou.parent_ou)
+    ), anscestor_depth AS (
+        SELECT  ou.id,
+                ou.parent_ou,
+                out.depth
+        FROM  actor.org_unit ou
+                JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+        WHERE ou.id = $2
+            UNION ALL
+        SELECT  ou.id,
+                ou.parent_ou,
+                out.depth
+        FROM  actor.org_unit ou
+                JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+                JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
+    ), descendants as (
+        SELECT ou.* FROM actor.org_unit ou JOIN descendant_depth USING (id)
+    )
+
+    SELECT ua.id, ua.name, ua.label_sortkey, MIN(ua.rank) AS rank FROM (
+        SELECT acn.id, aou.name, acn.label_sortkey,
+            RANK() OVER w
+        FROM asset.call_number acn
+            JOIN asset.copy acp ON (acn.id = acp.call_number)
+            JOIN descendants AS aou ON (acp.circ_lib = aou.id)
+        WHERE acn.record = ANY ($1)
+            AND acn.deleted IS FALSE
+            AND acp.deleted IS FALSE
+            AND CASE WHEN ('exclude_invisible_acn' = ANY($7)) THEN
+                EXISTS (
+                    SELECT 1
+                    FROM asset.opac_visible_copies
+                    WHERE copy_id = acp.id AND record = acn.record
+                ) ELSE TRUE END
+        GROUP BY acn.id, acp.status, aou.name, acn.label_sortkey, aou.id
+        WINDOW w AS (
+            ORDER BY
+                COALESCE(
+                    CASE WHEN aou.id = $2 THEN -20000 END,
+                    CASE WHEN aou.id = $6 THEN -10000 END,
+                    (SELECT distance - 5000
+                        FROM actor.org_unit_descendants_distance($6) as x
+                        WHERE x.id = aou.id AND $6 IN (
+                            SELECT q.id FROM actor.org_unit_descendants($2) as q)),
+                    (SELECT e.distance FROM actor.org_unit_descendants_distance($2) as e WHERE e.id = aou.id),
+                    1000
+                ),
+                evergreen.rank_cp_status(acp.status)
+        )
+    ) AS ua
+    GROUP BY ua.id, ua.name, ua.label_sortkey
+    ORDER BY rank, ua.name, ua.label_sortkey
+    LIMIT ($4 -> 'acn')::INT
+    OFFSET ($5 -> 'acn')::INT;
+$$ LANGUAGE SQL STABLE ROWS 10;
+
+CREATE OR REPLACE FUNCTION evergreen.ranked_volumes
+    ( bibid BIGINT, ouid INT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, pref_lib INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[] )
+    RETURNS TABLE (id BIGINT, name TEXT, label_sortkey TEXT, rank BIGINT)
+    AS $$ SELECT * FROM evergreen.ranked_volumes(ARRAY[$1],$2,$3,$4,$5,$6,$7) $$ LANGUAGE SQL STABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0886', :eg_version);
+
+INSERT INTO config.copy_status
+(id, name, holdable, opac_visible, copy_active, restrict_copy_delete)
+VALUES (17, 'Lost and Paid', FALSE, FALSE, FALSE, TRUE);
+
+INSERT INTO config.org_unit_setting_type
+(name, grp, label, description, datatype)
+VALUES
+('circ.use_lost_paid_copy_status',
+ 'circ',
+ oils_i18n_gettext('circ.use_lost_paid_copy_status',
+     'Use Lost and Paid copy status',
+     'coust', 'label'),
+ oils_i18n_gettext('circ.use_lost_paid_copy_status',
+     'Use Lost and Paid copy status when lost or long overdue billing is paid',
+     'coust', 'description'),
+ 'bool');
+
+SELECT evergreen.upgrade_deps_block_check('0887', :eg_version);
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field_list( marc TEXT, ff TEXT, use_default BOOL DEFAULT FALSE ) RETURNS TEXT[] AS $func$
+DECLARE
+    rtype       TEXT;
+    ff_pos      RECORD;
+    tag_data    RECORD;
+    val         TEXT;
+    collection  TEXT[] := '{}'::TEXT[];
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
+        IF ff_pos.tag = 'ldr' THEN
+            val := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF val IS NOT NULL THEN
+                val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
+                collection := collection || val;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+                collection := collection || val;
+            END LOOP;
+        END IF;
+        CONTINUE WHEN NOT use_default;
+        CONTINUE WHEN ARRAY_UPPER(collection, 1) > 0;
+        val := REPEAT( ff_pos.default_val, ff_pos.length );
+        collection := collection || val;
+    END LOOP;
+
+    RETURN collection;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT, use_default BOOL DEFAULT FALSE ) RETURNS TEXT AS $func$
+DECLARE
+    rtype       TEXT;
+    ff_pos      RECORD;
+    tag_data    RECORD;
+    val         TEXT;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
+        IF ff_pos.tag = 'ldr' THEN
+            val := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF val IS NOT NULL THEN
+                val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN val;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN val;
+            END LOOP;
+        END IF;
+        CONTINUE WHEN NOT use_default;
+        val := REPEAT( ff_pos.default_val, ff_pos.length );
+        RETURN val;
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT, use_default BOOL DEFAULT FALSE ) RETURNS SETOF biblio.record_ff_map AS $func$
+DECLARE
+    tag_data    TEXT;
+    rtype       TEXT;
+    ff_pos      RECORD;
+    output      biblio.record_ff_map%ROWTYPE;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
+        output.ff_name  := ff_pos.fixed_field;
+        output.ff_value := NULL;
+
+        IF ff_pos.tag = 'ldr' THEN
+            output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
+            IF output.ff_value IS NOT NULL THEN
+                output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
+                RETURN NEXT output;
+                output.ff_value := NULL;
+            END IF;
+        ELSE
+            FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+                output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
+                CONTINUE WHEN output.ff_value IS NULL AND NOT use_default;
+                IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
+                RETURN NEXT output;
+                output.ff_value := NULL;
+            END LOOP;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_fixed_field_list( rid BIGINT, ff TEXT ) RETURNS TEXT[] AS $func$
+    SELECT * FROM vandelay.marc21_extract_fixed_field_list( (SELECT marc FROM biblio.record_entry WHERE id = $1), $2, TRUE );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_fixed_field( rid BIGINT, ff TEXT ) RETURNS TEXT AS $func$
+    SELECT * FROM vandelay.marc21_extract_fixed_field( (SELECT marc FROM biblio.record_entry WHERE id = $1), $2, TRUE );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_all_fixed_fields( rid BIGINT ) RETURNS SETOF biblio.record_ff_map AS $func$
+    SELECT $1 AS record, ff_name, ff_value FROM vandelay.marc21_extract_all_fixed_fields( (SELECT marc FROM biblio.record_entry WHERE id = $1), TRUE );
+$func$ LANGUAGE SQL;
+
+DROP FUNCTION IF EXISTS vandelay.marc21_extract_fixed_field_list( text, text );
+DROP FUNCTION IF EXISTS vandelay.marc21_extract_fixed_field( text, text );
+DROP FUNCTION IF EXISTS vandelay.marc21_extract_all_fixed_fields( text );
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0888', :eg_version);
+
+DROP VIEW acq.lineitem_summary;
+
+CREATE VIEW acq.lineitem_summary AS
+    SELECT 
+        li.id AS lineitem, 
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE lineitem = li.id
+        ) AS item_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE recv_time IS NOT NULL AND lineitem = li.id
+        ) AS recv_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.cancel_reason acqcr ON (acqcr.id = lid.cancel_reason)
+            WHERE acqcr.keep_debits IS FALSE AND lineitem = li.id
+        ) AS cancel_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.cancel_reason acqcr ON (acqcr.id = lid.cancel_reason)
+            WHERE acqcr.keep_debits IS TRUE AND lineitem = li.id
+        ) AS delay_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE NOT debit.encumbrance AND lineitem = li.id
+        ) AS invoice_count,
+        (
+            SELECT COUNT(DISTINCT(lid.id)) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.claim claim ON (claim.lineitem_detail = lid.id)
+            WHERE lineitem = li.id
+        ) AS claim_count,
+        (
+            SELECT (COUNT(lid.id) * li.estimated_unit_price)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+            WHERE lid.cancel_reason IS NULL AND lineitem = li.id
+        ) AS estimated_amount,
+        (
+            SELECT SUM(debit.amount)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE debit.encumbrance AND lineitem = li.id
+        ) AS encumbrance_amount,
+        (
+            SELECT SUM(debit.amount)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE NOT debit.encumbrance AND lineitem = li.id
+        ) AS paid_amount
+
+        FROM acq.lineitem AS li;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0889', :eg_version);
+
+-- Update ACQ cancel reason names, but only those that 
+-- have not already been locally modified from stock values.
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Canceled: Invalid ISBN', 'acqcr', 'label')
+    WHERE id = 1 AND label = 'invalid_isbn';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Canceled: Postpone', 'acqcr', 'label')
+    WHERE id = 2 AND label = 'postpone';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Delivered but Lost', 'acqcr', 'label')
+    WHERE id = 3 AND label = 'delivered_but_lost';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Canceled: Deleted', 'acqcr', 'label')
+    WHERE id = 1002 AND label = 'Deleted';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Changed', 'acqcr', 'label')
+    WHERE id = 1003 AND label = 'Changed';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: No Action', 'acqcr', 'label')
+    WHERE id = 1004 AND label = 'No action';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Accepted without amendment', 'acqcr', 'label')
+    WHERE id = 1005 AND label = 'Accepted without amendment';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Canceled: Not Accepted', 'acqcr', 'label')
+    WHERE id = 1007 AND label = 'Not accepted';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Canceled: Not Found', 'acqcr', 'label')
+    WHERE id = 1010 AND label = 'Not found';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Accepted with amendment', 'acqcr', 'label')
+    WHERE id = 1024 AND label = 'Accepted with amendment, no confirmation required';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Split Quantity', 'acqcr', 'label')
+    WHERE id = 1211 AND label = 'Split quantity';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Ordered Quantity', 'acqcr', 'label')
+    WHERE id = 1221 AND label = 'Ordered quantity';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Pieces Delivered', 'acqcr', 'label')
+    WHERE id = 1246 AND label = 'Pieces delivered';
+
+UPDATE acq.cancel_reason 
+    SET label = oils_i18n_gettext(1,'Delayed: Backorder', 'acqcr', 'label')
+    WHERE id = 1283 AND label = 'Backorder quantity';
+
+-- action/trigger additions
+-- All following changes are only applied where the source data matches
+-- the stock data.  IOW, if a template has been locally modified, 
+-- it's left unchanged.
+
+DO $$
+BEGIN
+    -- avoid collisions by testing for the presence of the 
+    -- desired environment addition.
+
+    PERFORM 1 FROM action_trigger.environment
+        WHERE event_def = 4 AND path = 'lineitems.cancel_reason';
+    IF NOT FOUND THEN 
+        INSERT INTO action_trigger.environment (event_def, path) 
+            VALUES (4, 'lineitems.cancel_reason');
+    END IF;
+
+    PERFORM 1 FROM action_trigger.environment
+        WHERE event_def = 14 AND path = 'cancel_reason';
+    IF NOT FOUND THEN 
+        INSERT INTO action_trigger.environment (event_def, path) 
+            VALUES ( 14, 'cancel_reason' );
+    END IF;
+
+    PERFORM 1 FROM action_trigger.environment
+        WHERE event_def = 14 AND path = 'lineitem_details.cancel_reason';
+    IF NOT FOUND THEN 
+        INSERT INTO action_trigger.environment (event_def, path) 
+            VALUES ( 14, 'lineitem_details.cancel_reason' );
+    END IF;
+END $$;
+
+UPDATE action_trigger.event_definition SET template = 
+$$
+[%- USE date -%]
+[%-
+    # find a lineitem attribute by name and optional type
+    BLOCK get_li_attr;
+        FOR attr IN li.attributes;
+            IF attr.attr_name == attr_name;
+                IF !attr_type OR attr_type == attr.attr_type;
+                    attr.attr_value;
+                    LAST;
+                END;
+            END;
+        END;
+    END
+-%]
+
+<h2>Purchase Order: [% target.name %] ([% target.id %])</h2>
+<br/>
+date <b>[% date.format(date.now, '%Y%m%d') %]</b>
+<br/>
+
+<style>
+    table td { padding:5px; border:1px solid #aaa;}
+    table { width:95%; border-collapse:collapse; }
+    #vendor-notes { padding:5px; border:1px solid #aaa; }
+</style>
+<table id='vendor-table'>
+  <tr>
+    <td valign='top'>Vendor</td>
+    <td>
+      <div>[% target.provider.name %]</div>
+      <div>[% target.provider.addresses.0.street1 %]</div>
+      <div>[% target.provider.addresses.0.street2 %]</div>
+      <div>[% target.provider.addresses.0.city %]</div>
+      <div>[% target.provider.addresses.0.state %]</div>
+      <div>[% target.provider.addresses.0.country %]</div>
+      <div>[% target.provider.addresses.0.post_code %]</div>
+    </td>
+    <td valign='top'>Ship to / Bill to</td>
+    <td>
+      <div>[% target.ordering_agency.name %]</div>
+      <div>[% target.ordering_agency.billing_address.street1 %]</div>
+      <div>[% target.ordering_agency.billing_address.street2 %]</div>
+      <div>[% target.ordering_agency.billing_address.city %]</div>
+      <div>[% target.ordering_agency.billing_address.state %]</div>
+      <div>[% target.ordering_agency.billing_address.country %]</div>
+      <div>[% target.ordering_agency.billing_address.post_code %]</div>
+    </td>
+  </tr>
+</table>
+
+<br/><br/>
+<fieldset id='vendor-notes'>
+    <legend>Notes to the Vendor</legend>
+    <ul>
+    [% FOR note IN target.notes %]
+        [% IF note.vendor_public == 't' %]
+            <li>[% note.value %]</li>
+        [% END %]
+    [% END %]
+    </ul>
+</fieldset>
+<br/><br/>
+
+<table>
+  <thead>
+    <tr>
+      <th>PO#</th>
+      <th>ISBN or Item #</th>
+      <th>Title</th>
+      <th>Quantity</th>
+      <th>Unit Price</th>
+      <th>Line Total</th>
+      <th>Delayed / Canceled</th>
+      <th>Notes</th>
+    </tr>
+  </thead>
+  <tbody>
+
+  [% subtotal = 0 %]
+  [% FOR li IN target.lineitems %]
+
+  <tr>
+    [% count = li.lineitem_details.size %]
+    [% price = li.estimated_unit_price %]
+    [% litotal = (price * count) %]
+    [% subtotal = subtotal + litotal %]
+    [% 
+        ident_attr = helpers.get_li_order_ident(li.attributes);
+        SET ident_value = ident_attr.attr_value IF ident_attr;
+    %]
+    <td>[% target.id %]</td>
+    <td>[% ident_value %]</td>
+    <td>[% PROCESS get_li_attr attr_name = 'title' %]</td>
+    <td>[% count %]</td>
+    <td>[% price %]</td>
+    <td>[% litotal %]</td>
+    <td>[% li.cancel_reason.label %]</td>
+    <td>
+        <ul>
+        [% FOR note IN li.lineitem_notes %]
+            [% IF note.vendor_public == 't' %]
+                <li>[% note.value %]</li>
+            [% END %]
+        [% END %]
+        </ul>
+    </td>
+  </tr>
+  [% END %]
+  <tr>
+    <td/><td/><td/><td/>
+    <td>Subtotal</td>
+    <td>[% subtotal %]</td>
+  </tr>
+  </tbody>
+</table>
+
+<br/>
+
+Total Line Item Count: [% target.lineitems.size %]
+$$
+WHERE id = 4 AND template = 
+$$
+[%- USE date -%]
+[%-
+    # find a lineitem attribute by name and optional type
+    BLOCK get_li_attr;
+        FOR attr IN li.attributes;
+            IF attr.attr_name == attr_name;
+                IF !attr_type OR attr_type == attr.attr_type;
+                    attr.attr_value;
+                    LAST;
+                END;
+            END;
+        END;
+    END
+-%]
+
+<h2>Purchase Order: [% target.name %] ([% target.id %])</h2>
+<br/>
+date <b>[% date.format(date.now, '%Y%m%d') %]</b>
+<br/>
+
+<style>
+    table td { padding:5px; border:1px solid #aaa;}
+    table { width:95%; border-collapse:collapse; }
+    #vendor-notes { padding:5px; border:1px solid #aaa; }
+</style>
+<table id='vendor-table'>
+  <tr>
+    <td valign='top'>Vendor</td>
+    <td>
+      <div>[% target.provider.name %]</div>
+      <div>[% target.provider.addresses.0.street1 %]</div>
+      <div>[% target.provider.addresses.0.street2 %]</div>
+      <div>[% target.provider.addresses.0.city %]</div>
+      <div>[% target.provider.addresses.0.state %]</div>
+      <div>[% target.provider.addresses.0.country %]</div>
+      <div>[% target.provider.addresses.0.post_code %]</div>
+    </td>
+    <td valign='top'>Ship to / Bill to</td>
+    <td>
+      <div>[% target.ordering_agency.name %]</div>
+      <div>[% target.ordering_agency.billing_address.street1 %]</div>
+      <div>[% target.ordering_agency.billing_address.street2 %]</div>
+      <div>[% target.ordering_agency.billing_address.city %]</div>
+      <div>[% target.ordering_agency.billing_address.state %]</div>
+      <div>[% target.ordering_agency.billing_address.country %]</div>
+      <div>[% target.ordering_agency.billing_address.post_code %]</div>
+    </td>
+  </tr>
+</table>
+
+<br/><br/>
+<fieldset id='vendor-notes'>
+    <legend>Notes to the Vendor</legend>
+    <ul>
+    [% FOR note IN target.notes %]
+        [% IF note.vendor_public == 't' %]
+            <li>[% note.value %]</li>
+        [% END %]
+    [% END %]
+    </ul>
+</fieldset>
+<br/><br/>
+
+<table>
+  <thead>
+    <tr>
+      <th>PO#</th>
+      <th>ISBN or Item #</th>
+      <th>Title</th>
+      <th>Quantity</th>
+      <th>Unit Price</th>
+      <th>Line Total</th>
+      <th>Notes</th>
+    </tr>
+  </thead>
+  <tbody>
+
+  [% subtotal = 0 %]
+  [% FOR li IN target.lineitems %]
+
+  <tr>
+    [% count = li.lineitem_details.size %]
+    [% price = li.estimated_unit_price %]
+    [% litotal = (price * count) %]
+    [% subtotal = subtotal + litotal %]
+    [% 
+        ident_attr = helpers.get_li_order_ident(li.attributes);
+        SET ident_value = ident_attr.attr_value IF ident_attr;
+    %]
+    <td>[% target.id %]</td>
+    <td>[% ident_value %]</td>
+    <td>[% PROCESS get_li_attr attr_name = 'title' %]</td>
+    <td>[% count %]</td>
+    <td>[% price %]</td>
+    <td>[% litotal %]</td>
+    <td>
+        <ul>
+        [% FOR note IN li.lineitem_notes %]
+            [% IF note.vendor_public == 't' %]
+                <li>[% note.value %]</li>
+            [% END %]
+        [% END %]
+        </ul>
+    </td>
+  </tr>
+  [% END %]
+  <tr>
+    <td/><td/><td/><td/>
+    <td>Subtotal</td>
+    <td>[% subtotal %]</td>
+  </tr>
+  </tbody>
+</table>
+
+<br/>
+
+Total Line Item Count: [% target.lineitems.size %]
+$$;
+
+-- lineitem worksheet
+UPDATE action_trigger.event_definition SET template =
+$$
+[%- USE date -%]
+[%- SET li = target; -%]
+<div class="wrapper">
+    <div class="summary" style='font-size:110%; font-weight:bold;'>
+
+        <div>Title: [% helpers.get_li_attr("title", "", li.attributes) %]</div>
+        <div>Author: [% helpers.get_li_attr("author", "", li.attributes) %]</div>
+        <div class="count">Item Count: [% li.lineitem_details.size %]</div>
+        <div class="lineid">Lineitem ID: [% li.id %]</div>
+        <div>Open Holds: [% helpers.bre_open_hold_count(li.eg_bib_id) %]</div>
+        [% IF li.cancel_reason.label %]
+        <div>[% li.cancel_reason.label %]</div>
+        [% END %]
+
+        [% IF li.distribution_formulas.size > 0 %]
+            [% SET forms = [] %]
+            [% FOREACH form IN li.distribution_formulas; forms.push(form.formula.name); END %]
+            <div>Distribution Formulas: [% forms.join(',') %]</div>
+        [% END %]
+
+        [% IF li.lineitem_notes.size > 0 %]
+            Lineitem Notes:
+            <ul>
+                [%- FOR note IN li.lineitem_notes -%]
+                    <li>
+                    [% IF note.alert_text %]
+                        [% note.alert_text.code -%] 
+                        [% IF note.value -%]
+                            : [% note.value %]
+                        [% END %]
+                    [% ELSE %]
+                        [% note.value -%] 
+                    [% END %]
+                    </li>
+                [% END %]
+            </ul>
+        [% END %]
+    </div>
+    <br/>
+    <table>
+        <thead>
+            <tr>
+                <th>Branch</th>
+                <th>Barcode</th>
+                <th>Call Number</th>
+                <th>Fund</th>
+                <th>Shelving Location</th>
+                <th>Recd.</th>
+                <th>Notes</th>
+                <th>Delayed / Canceled</th>
+            </tr>
+        </thead>
+        <tbody>
+        <!-- set detail.owning_lib from fm object to org name -->
+        [% FOREACH detail IN li.lineitem_details %]
+            [% detail.owning_lib = detail.owning_lib.shortname %]
+        [% END %]
+
+        [% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
+            [% 
+                IF detail.eg_copy_id;
+                    SET copy = detail.eg_copy_id;
+                    SET cn_label = copy.call_number.label;
+                ELSE; 
+                    SET copy = detail; 
+                    SET cn_label = detail.cn_label;
+                END 
+            %]
+            <tr>
+                <!-- acq.lineitem_detail.id = [%- detail.id -%] -->
+                <td style='padding:5px;'>[% detail.owning_lib %]</td>
+                <td style='padding:5px;'>[% IF copy.barcode   %]<span class="barcode"  >[% detail.barcode   %]</span>[% END %]</td>
+                <td style='padding:5px;'>[% IF cn_label %]<span class="cn_label" >[% cn_label  %]</span>[% END %]</td>
+                <td style='padding:5px;'>[% IF detail.fund %]<span class="fund">[% detail.fund.code %] ([% detail.fund.year %])</span>[% END %]</td>
+                <td style='padding:5px;'>[% copy.location.name %]</td>
+                <td style='padding:5px;'>[% IF detail.recv_time %]<span class="recv_time">[% detail.recv_time %]</span>[% END %]</td>
+                <td style='padding:5px;'>[% detail.note %]</td>
+                <td style='padding:5px;'>[% detail.cancel_reason.label %]</td>
+            </tr>
+        [% END %]
+        </tbody>
+    </table>
+</div>
+$$
+WHERE id = 14 AND template = 
+$$
+[%- USE date -%]
+[%- SET li = target; -%]
+<div class="wrapper">
+    <div class="summary" style='font-size:110%; font-weight:bold;'>
+
+        <div>Title: [% helpers.get_li_attr("title", "", li.attributes) %]</div>
+        <div>Author: [% helpers.get_li_attr("author", "", li.attributes) %]</div>
+        <div class="count">Item Count: [% li.lineitem_details.size %]</div>
+        <div class="lineid">Lineitem ID: [% li.id %]</div>
+        <div>Open Holds: [% helpers.bre_open_hold_count(li.eg_bib_id) %]</div>
+
+        [% IF li.distribution_formulas.size > 0 %]
+            [% SET forms = [] %]
+            [% FOREACH form IN li.distribution_formulas; forms.push(form.formula.name); END %]
+            <div>Distribution Formulas: [% forms.join(',') %]</div>
+        [% END %]
+
+        [% IF li.lineitem_notes.size > 0 %]
+            Lineitem Notes:
+            <ul>
+                [%- FOR note IN li.lineitem_notes -%]
+                    <li>
+                    [% IF note.alert_text %]
+                        [% note.alert_text.code -%] 
+                        [% IF note.value -%]
+                            : [% note.value %]
+                        [% END %]
+                    [% ELSE %]
+                        [% note.value -%] 
+                    [% END %]
+                    </li>
+                [% END %]
+            </ul>
+        [% END %]
+    </div>
+    <br/>
+    <table>
+        <thead>
+            <tr>
+                <th>Branch</th>
+                <th>Barcode</th>
+                <th>Call Number</th>
+                <th>Fund</th>
+                <th>Shelving Location</th>
+                <th>Recd.</th>
+                <th>Notes</th>
+            </tr>
+        </thead>
+        <tbody>
+        <!-- set detail.owning_lib from fm object to org name -->
+        [% FOREACH detail IN li.lineitem_details %]
+            [% detail.owning_lib = detail.owning_lib.shortname %]
+        [% END %]
+
+        [% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
+            [% 
+                IF detail.eg_copy_id;
+                    SET copy = detail.eg_copy_id;
+                    SET cn_label = copy.call_number.label;
+                ELSE; 
+                    SET copy = detail; 
+                    SET cn_label = detail.cn_label;
+                END 
+            %]
+            <tr>
+                <!-- acq.lineitem_detail.id = [%- detail.id -%] -->
+                <td style='padding:5px;'>[% detail.owning_lib %]</td>
+                <td style='padding:5px;'>[% IF copy.barcode   %]<span class="barcode"  >[% detail.barcode   %]</span>[% END %]</td>
+                <td style='padding:5px;'>[% IF cn_label %]<span class="cn_label" >[% cn_label  %]</span>[% END %]</td>
+                <td style='padding:5px;'>[% IF detail.fund %]<span class="fund">[% detail.fund.code %] ([% detail.fund.year %])</span>[% END %]</td>
+                <td style='padding:5px;'>[% copy.location.name %]</td>
+                <td style='padding:5px;'>[% IF detail.recv_time %]<span class="recv_time">[% detail.recv_time %]</span>[% END %]</td>
+                <td style='padding:5px;'>[% detail.note %]</td>
+            </tr>
+        [% END %]
+        </tbody>
+    </table>
+</div>
+$$;
+
+SELECT evergreen.upgrade_deps_block_check('0890', :eg_version);
+
+CREATE OR REPLACE FUNCTION acq.transfer_fund(
+       old_fund   IN INT,
+       old_amount IN NUMERIC,     -- in currency of old fund
+       new_fund   IN INT,
+       new_amount IN NUMERIC,     -- in currency of new fund
+       user_id    IN INT,
+       xfer_note  IN TEXT         -- to be recorded in acq.fund_transfer
+       -- ,funding_source_in IN INT  -- if user wants to specify a funding source (see notes)
+) RETURNS VOID AS $$
+/* -------------------------------------------------------------------------------
+
+Function to transfer money from one fund to another.
+
+A transfer is represented as a pair of entries in acq.fund_allocation, with a
+negative amount for the old (losing) fund and a positive amount for the new
+(gaining) fund.  In some cases there may be more than one such pair of entries
+in order to pull the money from different funding sources, or more specifically
+from different funding source credits.  For each such pair there is also an
+entry in acq.fund_transfer.
+
+Since funding_source is a non-nullable column in acq.fund_allocation, we must
+choose a funding source for the transferred money to come from.  This choice
+must meet two constraints, so far as possible:
+
+1. The amount transferred from a given funding source must not exceed the
+amount allocated to the old fund by the funding source.  To that end we
+compare the amount being transferred to the amount allocated.
+
+2. We shouldn't transfer money that has already been spent or encumbered, as
+defined by the funding attribution process.  We attribute expenses to the
+oldest funding source credits first.  In order to avoid transferring that
+attributed money, we reverse the priority, transferring from the newest funding
+source credits first.  There can be no guarantee that this approach will
+avoid overcommitting a fund, but no other approach can do any better.
+
+In this context the age of a funding source credit is defined by the
+deadline_date for credits with deadline_dates, and by the effective_date for
+credits without deadline_dates, with the proviso that credits with deadline_dates
+are all considered "older" than those without.
+
+----------
+
+In the signature for this function, there is one last parameter commented out,
+named "funding_source_in".  Correspondingly, the WHERE clause for the query
+driving the main loop has an OR clause commented out, which references the
+funding_source_in parameter.
+
+If these lines are uncommented, this function will allow the user optionally to
+restrict a fund transfer to a specified funding source.  If the source
+parameter is left NULL, then there will be no such restriction.
+
+------------------------------------------------------------------------------- */ 
+DECLARE
+       same_currency      BOOLEAN;
+       currency_ratio     NUMERIC;
+       old_fund_currency  TEXT;
+       old_remaining      NUMERIC;  -- in currency of old fund
+       new_fund_currency  TEXT;
+       new_fund_active    BOOLEAN;
+       new_remaining      NUMERIC;  -- in currency of new fund
+       curr_old_amt       NUMERIC;  -- in currency of old fund
+       curr_new_amt       NUMERIC;  -- in currency of new fund
+       source_addition    NUMERIC;  -- in currency of funding source
+       source_deduction   NUMERIC;  -- in currency of funding source
+       orig_allocated_amt NUMERIC;  -- in currency of funding source
+       allocated_amt      NUMERIC;  -- in currency of fund
+       source             RECORD;
+BEGIN
+       --
+       -- Sanity checks
+       --
+       IF old_fund IS NULL THEN
+               RAISE EXCEPTION 'acq.transfer_fund: old fund id is NULL';
+       END IF;
+       --
+       IF old_amount IS NULL THEN
+               RAISE EXCEPTION 'acq.transfer_fund: amount to transfer is NULL';
+       END IF;
+       --
+       -- The new fund and its amount must be both NULL or both not NULL.
+       --
+       IF new_fund IS NOT NULL AND new_amount IS NULL THEN
+               RAISE EXCEPTION 'acq.transfer_fund: amount to transfer to receiving fund is NULL';
+       END IF;
+       --
+       IF new_fund IS NULL AND new_amount IS NOT NULL THEN
+               RAISE EXCEPTION 'acq.transfer_fund: receiving fund is NULL, its amount is not NULL';
+       END IF;
+       --
+       IF user_id IS NULL THEN
+               RAISE EXCEPTION 'acq.transfer_fund: user id is NULL';
+       END IF;
+       --
+       -- Initialize the amounts to be transferred, each denominated
+       -- in the currency of its respective fund.  They will be
+       -- reduced on each iteration of the loop.
+       --
+       old_remaining := old_amount;
+       new_remaining := new_amount;
+       --
+       -- RAISE NOTICE 'Transferring % in fund % to % in fund %',
+       --      old_amount, old_fund, new_amount, new_fund;
+       --
+       -- Get the currency types of the old and new funds.
+       --
+       SELECT
+               currency_type
+       INTO
+               old_fund_currency
+       FROM
+               acq.fund
+       WHERE
+               id = old_fund;
+       --
+       IF old_fund_currency IS NULL THEN
+               RAISE EXCEPTION 'acq.transfer_fund: old fund id % is not defined', old_fund;
+       END IF;
+       --
+       IF new_fund IS NOT NULL THEN
+               SELECT
+                       currency_type,
+                       active
+               INTO
+                       new_fund_currency,
+                       new_fund_active
+               FROM
+                       acq.fund
+               WHERE
+                       id = new_fund;
+               --
+               IF new_fund_currency IS NULL THEN
+                       RAISE EXCEPTION 'acq.transfer_fund: new fund id % is not defined', new_fund;
+               ELSIF NOT new_fund_active THEN
+                       --
+                       -- No point in putting money into a fund from whence you can't spend it
+                       --
+                       RAISE EXCEPTION 'acq.transfer_fund: new fund id % is inactive', new_fund;
+               END IF;
+               --
+               IF new_amount = old_amount THEN
+                       same_currency := true;
+                       currency_ratio := 1;
+               ELSE
+                       --
+                       -- We'll have to translate currency between funds.  We presume that
+                       -- the calling code has already applied an appropriate exchange rate,
+                       -- so we'll apply the same conversion to each sub-transfer.
+                       --
+                       same_currency := false;
+                       currency_ratio := new_amount / old_amount;
+               END IF;
+       END IF;
+       --
+       -- Identify the funding source(s) from which we want to transfer the money.
+       -- The principle is that we want to transfer the newest money first, because
+       -- we spend the oldest money first.  The priority for spending is defined
+       -- by a sort of the view acq.ordered_funding_source_credit.
+       --
+       FOR source in
+               SELECT
+                       ofsc.id,
+                       ofsc.funding_source,
+                       ofsc.amount,
+                       ofsc.amount * acq.exchange_ratio( fs.currency_type, old_fund_currency )
+                               AS converted_amt,
+                       fs.currency_type
+               FROM
+                       acq.ordered_funding_source_credit AS ofsc,
+                       acq.funding_source fs
+               WHERE
+                       ofsc.funding_source = fs.id
+                       and ofsc.funding_source IN
+                       (
+                               SELECT funding_source
+                               FROM acq.fund_allocation
+                               WHERE fund = old_fund
+                       )
+                       -- and
+                       -- (
+                       --      ofsc.funding_source = funding_source_in
+                       --      OR funding_source_in IS NULL
+                       -- )
+               ORDER BY
+                       ofsc.sort_priority desc,
+                       ofsc.sort_date desc,
+                       ofsc.id desc
+       LOOP
+               --
+               -- Determine how much money the old fund got from this funding source,
+               -- denominated in the currency types of the source and of the fund.
+               -- This result may reflect transfers from previous iterations.
+               --
+               SELECT
+                       COALESCE( sum( amount ), 0 ),
+                       COALESCE( sum( amount )
+                               * acq.exchange_ratio( source.currency_type, old_fund_currency ), 0 )
+               INTO
+                       orig_allocated_amt,     -- in currency of the source
+                       allocated_amt           -- in currency of the old fund
+               FROM
+                       acq.fund_allocation
+               WHERE
+                       fund = old_fund
+                       and funding_source = source.funding_source;
+               --      
+               -- Determine how much to transfer from this credit, in the currency
+               -- of the fund.   Begin with the amount remaining to be attributed:
+               --
+               curr_old_amt := old_remaining;
+               --
+               -- Can't attribute more than was allocated from the fund:
+               --
+               IF curr_old_amt > allocated_amt THEN
+                       curr_old_amt := allocated_amt;
+               END IF;
+               --
+               -- Can't attribute more than the amount of the current credit:
+               --
+               IF curr_old_amt > source.converted_amt THEN
+                       curr_old_amt := source.converted_amt;
+               END IF;
+               --
+               curr_old_amt := trunc( curr_old_amt, 2 );
+               --
+               old_remaining := old_remaining - curr_old_amt;
+               --
+               -- Determine the amount to be deducted, if any,
+               -- from the old allocation.
+               --
+               IF old_remaining > 0 THEN
+                       --
+                       -- In this case we're using the whole allocation, so use that
+                       -- amount directly instead of applying a currency translation
+                       -- and thereby inviting round-off errors.
+                       --
+                       source_deduction := - curr_old_amt;
+               ELSE 
+                       source_deduction := trunc(
+                               ( - curr_old_amt ) *
+                                       acq.exchange_ratio( old_fund_currency, source.currency_type ),
+                               2 );
+               END IF;
+               --
+               IF source_deduction <> 0 THEN
+                       --
+                       -- Insert negative allocation for old fund in fund_allocation,
+                       -- converted into the currency of the funding source
+                       --
+                       INSERT INTO acq.fund_allocation (
+                               funding_source,
+                               fund,
+                               amount,
+                               allocator,
+                               note
+                       ) VALUES (
+                               source.funding_source,
+                               old_fund,
+                               source_deduction,
+                               user_id,
+                               'Transfer to fund ' || new_fund
+                       );
+               END IF;
+               --
+               IF new_fund IS NOT NULL THEN
+                       --
+                       -- Determine how much to add to the new fund, in
+                       -- its currency, and how much remains to be added:
+                       --
+                       IF same_currency THEN
+                               curr_new_amt := curr_old_amt;
+                       ELSE
+                               IF old_remaining = 0 THEN
+                                       --
+                                       -- This is the last iteration, so nothing should be left
+                                       --
+                                       curr_new_amt := new_remaining;
+                                       new_remaining := 0;
+                               ELSE
+                                       curr_new_amt := trunc( curr_old_amt * currency_ratio, 2 );
+                                       new_remaining := new_remaining - curr_new_amt;
+                               END IF;
+                       END IF;
+                       --
+                       -- Determine how much to add, if any,
+                       -- to the new fund's allocation.
+                       --
+                       IF old_remaining > 0 THEN
+                               --
+                               -- In this case we're using the whole allocation, so use that amount
+                               -- amount directly instead of applying a currency translation and
+                               -- thereby inviting round-off errors.
+                               --
+                               source_addition := curr_new_amt;
+                       ELSIF source.currency_type = old_fund_currency THEN
+                               --
+                               -- In this case we don't need a round trip currency translation,
+                               -- thereby inviting round-off errors:
+                               --
+                               source_addition := curr_old_amt;
+                       ELSE 
+                               source_addition := trunc(
+                                       curr_new_amt *
+                                               acq.exchange_ratio( new_fund_currency, source.currency_type ),
+                                       2 );
+                       END IF;
+                       --
+                       IF source_addition <> 0 THEN
+                               --
+                               -- Insert positive allocation for new fund in fund_allocation,
+                               -- converted to the currency of the founding source
+                               --
+                               INSERT INTO acq.fund_allocation (
+                                       funding_source,
+                                       fund,
+                                       amount,
+                                       allocator,
+                                       note
+                               ) VALUES (
+                                       source.funding_source,
+                                       new_fund,
+                                       source_addition,
+                                       user_id,
+                                       'Transfer from fund ' || old_fund
+                               );
+                       END IF;
+               END IF;
+               --
+               IF trunc( curr_old_amt, 2 ) <> 0
+               OR trunc( curr_new_amt, 2 ) <> 0 THEN
+                       --
+                       -- Insert row in fund_transfer, using amounts in the currency of the funds
+                       --
+                       INSERT INTO acq.fund_transfer (
+                               src_fund,
+                               src_amount,
+                               dest_fund,
+                               dest_amount,
+                               transfer_user,
+                               note,
+                               funding_source_credit
+                       ) VALUES (
+                               old_fund,
+                               trunc( curr_old_amt, 2 ),
+                               new_fund,
+                               trunc( curr_new_amt, 2 ),
+                               user_id,
+                               xfer_note,
+                               source.id
+                       );
+               END IF;
+               --
+               if old_remaining <= 0 THEN
+                       EXIT;                   -- Nothing more to be transferred
+               END IF;
+       END LOOP;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT evergreen.upgrade_deps_block_check('0891', :eg_version);
+
+UPDATE permission.perm_list
+SET description = 'Allows a user to process and verify URLs'
+WHERE code = 'URL_VERIFY';
+
+SELECT evergreen.upgrade_deps_block_check('0892', :eg_version);
+
+CREATE OR REPLACE VIEW metabib.record_attr_flat AS
+    SELECT  v.source AS id,
+            m.attr AS attr,
+            m.value AS value
+      FROM  metabib.record_attr_vector_list v
+            LEFT JOIN metabib.uncontrolled_record_attr_value m ON ( m.id = ANY( v.vlist ) )
+        UNION
+    SELECT  v.source AS id,
+            c.ctype AS attr,
+            c.code AS value
+      FROM  metabib.record_attr_vector_list v
+            LEFT JOIN config.coded_value_map c ON ( c.id = ANY( v.vlist ) );
+
+CREATE OR REPLACE FUNCTION unapi.mmr_mra (
+    obj_id BIGINT,
+    format TEXT,
+    ename TEXT,
+    includes TEXT[],
+    org TEXT,
+    depth INT DEFAULT NULL,
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+) RETURNS XML AS $F$
+    SELECT  XMLELEMENT(
+        name attributes,
+        XMLATTRIBUTES(
+            CASE WHEN $9 THEN 'http://open-ils.org/spec/indexing/v1' ELSE NULL END AS xmlns,
+            'tag:open-ils.org:U2@mmr/' || $1 AS metarecord
+        ),
+        (SELECT XMLAGG(foo.y)
+          FROM (
+            WITH sourcelist AS (
+                WITH aou AS (SELECT COALESCE(id, (evergreen.org_top()).id) AS id
+                    FROM actor.org_unit WHERE shortname = $5 LIMIT 1)
+                SELECT source
+                FROM metabib.metarecord_source_map, aou
+                WHERE metarecord = $1 AND (
+                    EXISTS (
+                        SELECT 1 FROM asset.opac_visible_copies
+                        WHERE record = source AND circ_lib IN (
+                            SELECT id FROM actor.org_unit_descendants(aou.id, $6))
+                        LIMIT 1
+                    )
+                    OR EXISTS (SELECT 1 FROM located_uris(source, aou.id, $10) LIMIT 1)
+                )
+            )
+            SELECT  cmra.aid,
+                    XMLELEMENT(
+                        name field,
+                        XMLATTRIBUTES(
+                            cmra.attr AS name,
+                            cmra.value AS "coded-value",
+                            cmra.aid AS "cvmid",
+                            rad.composite,
+                            rad.multi,
+                            rad.filter,
+                            rad.sorter
+                        ),
+                        cmra.value
+                    )
+              FROM  (
+                SELECT  v.source AS id,
+                        c.id AS aid,
+                        c.ctype AS attr,
+                        c.code AS value
+                  FROM  metabib.record_attr_vector_list v
+                        JOIN config.coded_value_map c ON ( c.id = ANY( v.vlist ) )
+                ) AS cmra
+                    JOIN config.record_attr_definition rad ON (cmra.attr = rad.name)
+                    JOIN sourcelist ON (cmra.id = sourcelist.source)
+                UNION ALL
+            SELECT  umra.aid,
+                    XMLELEMENT(
+                        name field,
+                        XMLATTRIBUTES(
+                            umra.attr AS name,
+                            rad.composite,
+                            rad.multi,
+                            rad.filter,
+                            rad.sorter
+                        ),
+                        umra.value
+                    )
+              FROM  (
+                SELECT  v.source AS id,
+                        m.id AS aid,
+                        m.attr AS attr,
+                        m.value AS value
+                  FROM  metabib.record_attr_vector_list v
+                        JOIN metabib.uncontrolled_record_attr_value m ON ( m.id = ANY( v.vlist ) )
+                ) AS umra
+                    JOIN config.record_attr_definition rad ON (umra.attr = rad.name)
+                    JOIN sourcelist ON (umra.id = sourcelist.source)
+                ORDER BY 1
+
+            )foo(id,y)
+        )
+    )
+$F$ LANGUAGE SQL STABLE;
+
+SELECT evergreen.upgrade_deps_block_check('0893', :eg_version); 
+
+CREATE OR REPLACE VIEW reporter.simple_record AS
+SELECT  r.id,
+    s.metarecord,
+    r.fingerprint,
+    r.quality,
+    r.tcn_source,
+    r.tcn_value,
+    title.value AS title,
+    uniform_title.value AS uniform_title,
+    author.value AS author,
+    publisher.value AS publisher,
+    SUBSTRING(pubdate.value FROM $$\d+$$) AS pubdate,
+    series_title.value AS series_title,
+    series_statement.value AS series_statement,
+    summary.value AS summary,
+    ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) AS isbn,
+    ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) AS issn,
+    ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '650' AND subfield = 'a' AND record = r.id)) AS topic_subject,
+    ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '651' AND subfield = 'a' AND record = r.id)) AS geographic_subject,
+    ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '655' AND subfield = 'a' AND record = r.id)) AS genre,
+    ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '600' AND subfield = 'a' AND record = r.id)) AS name_subject,
+    ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '610' AND subfield = 'a' AND record = r.id)) AS corporate_subject,
+    ARRAY((SELECT value FROM metabib.full_rec WHERE tag = '856' AND subfield IN ('3','y','u') AND record = r.id ORDER BY CASE WHEN subfield IN ('3','y') THEN 0 ELSE 1 END)) AS external_uri
+  FROM  biblio.record_entry r
+    JOIN metabib.metarecord_source_map s ON (s.source = r.id)
+    LEFT JOIN metabib.full_rec uniform_title ON (r.id = uniform_title.record AND uniform_title.tag = '240' AND uniform_title.subfield = 'a')
+    LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
+    LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag = '100' AND author.subfield = 'a')
+    LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND (publisher.tag = '260' OR (publisher.tag = '264' AND publisher.ind2 = '1')) AND publisher.subfield = 'b')
+    LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND (pubdate.tag = '260' OR (publisher.tag = '264' AND publisher.ind2 = '1')) AND pubdate.subfield = 'c')
+    LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
+    LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
+    LEFT JOIN metabib.full_rec series_title ON (r.id = series_title.record AND series_title.tag IN ('830','440') AND series_title.subfield = 'a')
+    LEFT JOIN metabib.full_rec series_statement ON (r.id = series_statement.record AND series_statement.tag = '490' AND series_statement.subfield = 'a')
+    LEFT JOIN metabib.full_rec summary ON (r.id = summary.record AND summary.tag = '520' AND summary.subfield = 'a')
+  GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14;
+
+SELECT evergreen.upgrade_deps_block_check('0894', :eg_version);
+
+CREATE INDEX m_b_voider_idx ON money.billing (voider);
+
+SELECT evergreen.upgrade_deps_block_check('0895', :eg_version);
+
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('File', '008', 'COM', 26, 1, 'u');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('File', '006', 'COM', 9, 1, 'u');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Freq', '008', 'SER', 18, 1, ' ');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Freq', '006', 'SER', 1, 1, ' ');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Regl', '008', 'SER', 19, 1, ' ');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Regl', '006', 'SER', 2, 1, ' ');
+
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('file','File','File');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('freq','Freq','Freq');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('regl','Regl','Regl');
+
+SELECT evergreen.upgrade_deps_block_check('0896', :eg_version);
+
+CREATE OR REPLACE FUNCTION asset.acp_location_fixer()
+RETURNS TRIGGER AS $$
+DECLARE
+    new_copy_location INT;
+BEGIN
+    IF (TG_OP = 'UPDATE') THEN
+        IF NEW.location = OLD.location AND NEW.call_number = OLD.call_number AND NEW.circ_lib = OLD.circ_lib THEN
+            RETURN NEW;
+        END IF;
+    END IF;
+    SELECT INTO new_copy_location acpl.id FROM asset.copy_location acpl JOIN actor.org_unit_ancestors_distance((SELECT owning_lib FROM asset.call_number WHERE id = NEW.call_number)) aouad ON acpl.owning_lib = aouad.id WHERE name = (SELECT name FROM asset.copy_location WHERE id = NEW.location) ORDER BY distance LIMIT 1;
+    IF new_copy_location IS NULL THEN
+        SELECT INTO new_copy_location acpl.id FROM asset.copy_location acpl JOIN actor.org_unit_ancestors_distance(NEW.circ_lib) aouad ON acpl.owning_lib = aouad.id WHERE name = (SELECT name FROM asset.copy_location WHERE id = NEW.location) ORDER BY distance LIMIT 1;
+    END IF;
+    IF new_copy_location IS NOT NULL THEN
+        NEW.location = new_copy_location;
+    END IF;
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS acp_location_fixer_trig ON asset.copy;
+
+CREATE TRIGGER acp_location_fixer_trig
+    BEFORE INSERT OR UPDATE OF location, call_number, circ_lib ON asset.copy
+    FOR EACH ROW EXECUTE PROCEDURE asset.acp_location_fixer();
+
+SELECT evergreen.upgrade_deps_block_check('0897', :eg_version);
+
+CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete() RETURNS TRIGGER AS $BODY$
+DECLARE
+    ashs    authority.simple_heading%ROWTYPE;
+    mbe_row metabib.browse_entry%ROWTYPE;
+    mbe_id  BIGINT;
+    ash_id  BIGINT;
+BEGIN
+
+    IF NEW.deleted IS TRUE THEN -- If this authority is deleted
+        DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
+          -- Should remove matching $0 from controlled fields at the same time?
+
+        -- XXX What do we about the actual linking subfields present in
+        -- authority records that target this one when this happens?
+        DELETE FROM authority.authority_linking
+            WHERE source = NEW.id OR target = NEW.id;
+
+        RETURN NEW; -- and we're done
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+
+        -- Unless there's a setting stopping us, propagate these updates to any linked bib records
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_auto_update' AND enabled;
+
+        IF NOT FOUND THEN
+            PERFORM authority.propagate_changes(NEW.id);
+        END IF;
+
+        DELETE FROM authority.simple_heading WHERE record = NEW.id;
+        DELETE FROM authority.authority_linking WHERE source = NEW.id;
+    END IF;
+
+    INSERT INTO authority.authority_linking (source, target, field)
+        SELECT source, target, field FROM authority.calculate_authority_linking(
+            NEW.id, NEW.control_set, NEW.marc::XML
+        );
+
+    FOR ashs IN SELECT * FROM authority.simple_heading_set(NEW.marc) LOOP
+
+        INSERT INTO authority.simple_heading (record,atag,value,sort_value)
+            VALUES (ashs.record, ashs.atag, ashs.value, ashs.sort_value);
+        ash_id := CURRVAL('authority.simple_heading_id_seq'::REGCLASS);
+
+        SELECT INTO mbe_row * FROM metabib.browse_entry
+            WHERE value = ashs.value AND sort_value = ashs.sort_value;
+
+        IF FOUND THEN
+            mbe_id := mbe_row.id;
+        ELSE
+            INSERT INTO metabib.browse_entry
+                ( value, sort_value ) VALUES
+                ( ashs.value, ashs.sort_value );
+
+            mbe_id := CURRVAL('metabib.browse_entry_id_seq'::REGCLASS);
+        END IF;
+
+        INSERT INTO metabib.browse_entry_simple_heading_map (entry,simple_heading) VALUES (mbe_id,ash_id);
+
+    END LOOP;
+
+    -- Flatten and insert the afr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM authority.reingest_authority_full_rec(NEW.id);
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
+        END IF;
+    END IF;
+
+    RETURN NEW;
+END;
+$BODY$ LANGUAGE plpgsql;
+
+COMMIT;
+
+\qecho **** If upgrading from Evergreen 2.3 or before, now is the time to run
+\qecho **** Open-ILS/src/sql/Pg/version-upgrade/2.3-2.4-supplemental.sh, which
+\qecho **** contains additional required SQL to complete your Evergreen upgrade!
+\qecho
+\qecho **** If upgrading from Evergreen 2.4.0, you will need to reingest your
+\qecho **** full data set.  In order to allow this to continue without locking
+\qecho **** your entire bibliographic data set, consider generating an SQL script
+\qecho **** with the following query, and running that via psql:
+\qecho
+\qecho '\\t'
+\qecho '\\o /tmp/reingest-2.4.1.sql'
+\qecho 'SELECT ''select metabib.reingest_metabib_field_entries('' || id || '');'' FROM biblio.record_entry WHERE NOT DELETED AND id > 0;'
+\qecho '\\o'
+\qecho '\\t'
+\qecho
+
+\qecho This is a browse-only reingest of your bib records. It may take a while.
+\qecho You may cancel now without losing the effect of the rest of the
+\qecho upgrade script, and arrange the reingest later.
+\qecho .
+SELECT metabib.reingest_metabib_field_entries(id, TRUE, FALSE, TRUE)
+    FROM biblio.record_entry;
+
+-- Not running ARRAY_ACCUM() changes from example.reporter-extension.sql since these are
+-- not installed by default, but including a helpful note.
+
+\qecho 'There were also ARRAY_ACCUM() changes in example.reporter-extension.sql'
+\qecho 'Please run that script again if you use it in your system'
+\qecho 'to apply new changes.'
+
+\qecho 'We dropped reporter.classic_current_circ earlier from the'
+\qecho 'example.reporter-extension.sql sample. You will need to'
+\qecho 'run it again to recreate that custom reporter view.'
+
+
+-- regenerate sort keys for any dewey call numbers
+UPDATE asset.call_number SET id = id WHERE label_class = 2;