[open-ils-commits] [GIT] Evergreen ILS branch master updated. d880f92a2f7be8a4fcc49d90a278a0ed22f971d4

Evergreen Git git at git.evergreen-ils.org
Thu May 10 12:41:43 EDT 2012


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  d880f92a2f7be8a4fcc49d90a278a0ed22f971d4 (commit)
      from  305f63cb721f975762bbcde1025f62bc5168cc67 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit d880f92a2f7be8a4fcc49d90a278a0ed22f971d4
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Thu May 10 11:05:54 2012 -0400

    Version Upgrade Cleanup
    
    Merge tacked on transactions into the "primary" transaction.
    Add in 0708 for tracking purposes.
    
    Separate 0672 and 0679+0680 in the version-to-version upgrade script
    They're all potentially slow.
    
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/src/sql/Pg/version-upgrade/2.1-2.2-upgrade-db.sql b/Open-ILS/src/sql/Pg/version-upgrade/2.1-2.2-upgrade-db.sql
index 3086b50..de6d5c5 100644
--- a/Open-ILS/src/sql/Pg/version-upgrade/2.1-2.2-upgrade-db.sql
+++ b/Open-ILS/src/sql/Pg/version-upgrade/2.1-2.2-upgrade-db.sql
@@ -8384,3404 +8384,161 @@ SELECT evergreen.upgrade_deps_block_check('0671', :eg_version);
 ALTER TABLE asset.copy_location
     ADD COLUMN checkin_alert BOOL NOT NULL DEFAULT FALSE;
 
--- Evergreen DB patch 0672.fix-nonfiling-titles.sql
---
--- Titles that begin with non-filing articles using apostrophes
--- (for example, "L'armée") get spaces injected between the article
--- and the subsequent text, which then breaks searching for titles
--- beginning with those articles.
---
--- This patch adds a nonfiling title element to MODS32 that can then
--- be used to retrieve the title proper without affecting the spaces
--- in the title. It's what we want, what we really really want, for
--- title searches.
+-- Evergreen DB patch 0673.data.acq-cancel-reason-cleanup.sql
 --
 
-
 -- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0672', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('0673', :eg_version);
 
--- Update the XPath definition before the titleNonfiling element exists;
--- but are you really going to read through the whole XSL below before
--- seeing this important bit?
+DELETE FROM
+    acq.cancel_reason
+WHERE
+    -- any entries with id >= 2000 were added locally.  
+    id < 2000 
+
+    -- these cancel_reason's are actively used by the system
+    AND id NOT IN (1, 2, 3, 1002, 1003, 1004, 1005, 1010, 1024, 1211, 1221, 1246, 1283)
+
+    -- don't delete any cancel_reason's that may be in use locally
+    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.user_request WHERE cancel_reason IS NOT NULL)
+    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.purchase_order WHERE cancel_reason IS NOT NULL)
+    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem WHERE cancel_reason IS NOT NULL)
+    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem_detail WHERE cancel_reason IS NOT NULL)
+    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_lineitem_history WHERE cancel_reason IS NOT NULL)
+    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_purchase_order_history WHERE cancel_reason IS NOT NULL);
+
+
+SELECT evergreen.upgrade_deps_block_check('0674', :eg_version);
+
+ALTER TABLE config.copy_status
+	  ADD COLUMN restrict_copy_delete BOOL NOT NULL DEFAULT FALSE;
+
+UPDATE config.copy_status
+SET restrict_copy_delete = TRUE
+WHERE id IN (1,3,6,8);
+
+INSERT INTO permission.perm_list (id, code, description) VALUES (
+    520,
+    'COPY_DELETE_WARNING.override',
+    'Allow a user to override warnings about deleting copies in problematic situations.'
+);
+
+
+SELECT evergreen.upgrade_deps_block_check('0675', :eg_version);
+
+-- set expected row count to low value to avoid problem
+-- where use of this function by the circ tagging feature
+-- results in full scans of asset.call_number
+CREATE OR REPLACE FUNCTION action.usr_visible_circ_copies( INTEGER ) RETURNS SETOF BIGINT AS $$
+    SELECT DISTINCT(target_copy) FROM action.usr_visible_circs($1)
+$$ LANGUAGE SQL ROWS 10;
+
+
+SELECT evergreen.upgrade_deps_block_check('0676', :eg_version);
+
+INSERT INTO config.global_flag (name, label, enabled, value) VALUES (
+    'opac.use_autosuggest',
+    'OPAC: Show auto-completing suggestions dialog under basic search box (put ''opac_visible'' into the value field to limit suggestions to OPAC-visible items, or blank the field for a possible performance improvement)',
+    TRUE,
+    'opac_visible'
+);
+
+CREATE TABLE metabib.browse_entry (
+    id BIGSERIAL PRIMARY KEY,
+    value TEXT unique,
+    index_vector tsvector
+);
+--Skip this, will be created differently later
+--CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIST (index_vector);
+CREATE TRIGGER metabib_browse_entry_fti_trigger
+    BEFORE INSERT OR UPDATE ON metabib.browse_entry
+    FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
+
+
+CREATE TABLE metabib.browse_entry_def_map (
+    id BIGSERIAL PRIMARY KEY,
+    entry BIGINT REFERENCES metabib.browse_entry (id),
+    def INT REFERENCES config.metabib_field (id),
+    source BIGINT REFERENCES biblio.record_entry (id)
+);
+
+ALTER TABLE config.metabib_field ADD COLUMN browse_field BOOLEAN DEFAULT TRUE NOT NULL;
+ALTER TABLE config.metabib_field ADD COLUMN browse_xpath TEXT;
+
+ALTER TABLE config.metabib_class ADD COLUMN bouyant BOOLEAN DEFAULT FALSE NOT NULL;
+ALTER TABLE config.metabib_class ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
+ALTER TABLE config.metabib_field ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
+
+-- one good exception to default true:
 UPDATE config.metabib_field
-    SET xpath = $$//mods32:mods/mods32:titleNonfiling[mods32:title and not (@type)]$$,
-        format = 'mods32'
-    WHERE field_class = 'title' AND name = 'proper';
+    SET browse_field = FALSE
+    WHERE (field_class = 'keyword' AND name = 'keyword') OR
+        (field_class = 'subject' AND name = 'complete');
 
-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
+-- AFTER UPDATE OR INSERT trigger for biblio.record_entry
+-- We're only touching it here to add a DELETE statement to the IF NEW.deleted
+-- block.
 
-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
+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
 
-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
+    IF NEW.deleted IS TRUE THEN -- If this bib is deleted
+        DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
+        DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
+        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;
 
-Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
 
-Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
+        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;
 
-Revision 1.7 2004/03/25 08:29 jrad
+    -- 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;
 
-Revision 1.6 various validation fixes 2004/02/20 ntra
+    -- 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);
 
-Revision 1.5  2003/10/02 16:18:58  ntra
-MODS2 to MODS3 updates, language unstacking and 
-de-duping, chopPunctuation expanded
+        -- 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
 
-Revision 1.3  2003/04/03 00:07:19  ntra
-Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
+                IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+                    SELECT  ARRAY_TO_STRING(ARRAY_ACCUM(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;
 
-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, at ind2)"/>
-						</nonSort>
-						<title>
-							<xsl:value-of select="substring($titleChop, at 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']">
-			<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: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:variable name="str">
-						<xsl:for-each select="marc:subfield">
-							<xsl:if test="(contains('adfklmor', at 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="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="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="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="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="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="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">
-				<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=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>
-				<titleInfo>
-					<title>
-						<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>
-					</title>
-					<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">
-				<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: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: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, at code) and following-sibling::marc:subfield[@code=$axis])      or (contains($afterCodes, at 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="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: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">
-				<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>
-			<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>
-					<xsl:call-template name="part"></xsl:call-template>
-				</title>
-			</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="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="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: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="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';
-
--- 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 = decode_utf8(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;
-
-CREATE OR REPLACE FUNCTION public.search_normalize_keep_comma( TEXT ) RETURNS TEXT AS $func$
-        SELECT public.search_normalize($1,'a');
-$func$ LANGUAGE SQL STRICT IMMUTABLE;
-
-CREATE OR REPLACE FUNCTION public.search_normalize( TEXT ) RETURNS TEXT AS $func$
-	SELECT public.search_normalize($1,'');
-$func$ LANGUAGE 'sql' STRICT IMMUTABLE;
-
-INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
-	'Search Normalize',
-	'Apply search normalization rules to the extracted text. A less extreme version of NACO normalization.',
-	'search_normalize',
-	0
-);
-
-UPDATE config.metabib_field_index_norm_map
-    SET norm = (
-        SELECT id FROM config.index_normalizer WHERE func = 'search_normalize'
-    )
-    WHERE norm = (
-        SELECT id FROM config.index_normalizer WHERE func = 'naco_normalize'
-    )
-;
-
-
--- This could take a long time if you have a very non-English bib database
--- Run it outside of a transaction to avoid lock escalation
-SELECT metabib.reingest_metabib_field_entries(record)
-    FROM metabib.full_rec
-    WHERE tag = '245'
-    AND subfield = 'a'
-    AND value LIKE '%''%'
-;
--- Evergreen DB patch 0673.data.acq-cancel-reason-cleanup.sql
---
-
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0673', :eg_version);
-
-DELETE FROM
-    acq.cancel_reason
-WHERE
-    -- any entries with id >= 2000 were added locally.  
-    id < 2000 
-
-    -- these cancel_reason's are actively used by the system
-    AND id NOT IN (1, 2, 3, 1002, 1003, 1004, 1005, 1010, 1024, 1211, 1221, 1246, 1283)
-
-    -- don't delete any cancel_reason's that may be in use locally
-    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.user_request WHERE cancel_reason IS NOT NULL)
-    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.purchase_order WHERE cancel_reason IS NOT NULL)
-    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem WHERE cancel_reason IS NOT NULL)
-    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.lineitem_detail WHERE cancel_reason IS NOT NULL)
-    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_lineitem_history WHERE cancel_reason IS NOT NULL)
-    AND id NOT IN (SELECT DISTINCT(cancel_reason) FROM acq.acq_purchase_order_history WHERE cancel_reason IS NOT NULL);
-
-
-SELECT evergreen.upgrade_deps_block_check('0674', :eg_version);
-
-ALTER TABLE config.copy_status
-	  ADD COLUMN restrict_copy_delete BOOL NOT NULL DEFAULT FALSE;
-
-UPDATE config.copy_status
-SET restrict_copy_delete = TRUE
-WHERE id IN (1,3,6,8);
-
-INSERT INTO permission.perm_list (id, code, description) VALUES (
-    520,
-    'COPY_DELETE_WARNING.override',
-    'Allow a user to override warnings about deleting copies in problematic situations.'
-);
-
-
-SELECT evergreen.upgrade_deps_block_check('0675', :eg_version);
-
--- set expected row count to low value to avoid problem
--- where use of this function by the circ tagging feature
--- results in full scans of asset.call_number
-CREATE OR REPLACE FUNCTION action.usr_visible_circ_copies( INTEGER ) RETURNS SETOF BIGINT AS $$
-    SELECT DISTINCT(target_copy) FROM action.usr_visible_circs($1)
-$$ LANGUAGE SQL ROWS 10;
-
-
-SELECT evergreen.upgrade_deps_block_check('0676', :eg_version);
-
-INSERT INTO config.global_flag (name, label, enabled, value) VALUES (
-    'opac.use_autosuggest',
-    'OPAC: Show auto-completing suggestions dialog under basic search box (put ''opac_visible'' into the value field to limit suggestions to OPAC-visible items, or blank the field for a possible performance improvement)',
-    TRUE,
-    'opac_visible'
-);
-
-CREATE TABLE metabib.browse_entry (
-    id BIGSERIAL PRIMARY KEY,
-    value TEXT unique,
-    index_vector tsvector
-);
---Skip this, will be created differently later
---CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIST (index_vector);
-CREATE TRIGGER metabib_browse_entry_fti_trigger
-    BEFORE INSERT OR UPDATE ON metabib.browse_entry
-    FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
-
-
-CREATE TABLE metabib.browse_entry_def_map (
-    id BIGSERIAL PRIMARY KEY,
-    entry BIGINT REFERENCES metabib.browse_entry (id),
-    def INT REFERENCES config.metabib_field (id),
-    source BIGINT REFERENCES biblio.record_entry (id)
-);
-
-ALTER TABLE config.metabib_field ADD COLUMN browse_field BOOLEAN DEFAULT TRUE NOT NULL;
-ALTER TABLE config.metabib_field ADD COLUMN browse_xpath TEXT;
-
-ALTER TABLE config.metabib_class ADD COLUMN bouyant BOOLEAN DEFAULT FALSE NOT NULL;
-ALTER TABLE config.metabib_class ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
-ALTER TABLE config.metabib_field ADD COLUMN restrict BOOLEAN DEFAULT FALSE NOT NULL;
-
--- one good exception to default true:
-UPDATE config.metabib_field
-    SET browse_field = FALSE
-    WHERE (field_class = 'keyword' AND name = 'keyword') OR
-        (field_class = 'subject' AND name = 'complete');
-
--- AFTER UPDATE OR INSERT trigger for biblio.record_entry
--- We're only touching it here to add a DELETE statement to the IF NEW.deleted
--- block.
-
-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
-        DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
-        DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
-        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  ARRAY_TO_STRING(ARRAY_ACCUM(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.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
 
@@ -12726,3412 +9483,6661 @@ INSERT INTO vandelay.merge_profile (owner, name, replace_spec)
 INSERT INTO vandelay.merge_profile (owner, name, preserve_spec) 
     VALUES (1, 'Full Overlay', '901c');
 
-SELECT evergreen.upgrade_deps_block_check('0679', :eg_version);
+-- Evergreen DB patch 0681.schema.user-activity.sql
+--
 
--- Address typo in column name
-ALTER TABLE config.metabib_class ADD COLUMN buoyant BOOL DEFAULT FALSE NOT NULL;
-UPDATE config.metabib_class SET buoyant = bouyant;
-ALTER TABLE config.metabib_class DROP COLUMN bouyant;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0681', :eg_version);
 
-CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
+-- SCHEMA --
+
+CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
+
+CREATE TABLE config.usr_activity_type (
+    id          SERIAL                      PRIMARY KEY, 
+    ewho        TEXT,
+    ewhat       TEXT,
+    ehow        TEXT,
+    label       TEXT                        NOT NULL, -- i18n
+    egroup      config.usr_activity_group   NOT NULL,
+    enabled     BOOL                        NOT NULL DEFAULT TRUE,
+    transient   BOOL                        NOT NULL DEFAULT FALSE,
+    CONSTRAINT  one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
+);
+
+CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type 
+    (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
+
+CREATE TABLE actor.usr_activity (
+    id          BIGSERIAL   PRIMARY KEY,
+    usr         INT         REFERENCES actor.usr (id) ON DELETE SET NULL,
+    etype       INT         NOT NULL REFERENCES config.usr_activity_type (id),
+    event_time  TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+-- remove transient activity entries on insert of new entries
+CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
+BEGIN
+    DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
+        WHERE atype.transient AND 
+            NEW.etype = atype.id AND
+            act.etype = atype.id AND
+            act.usr = NEW.usr;
+    RETURN NEW;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER remove_transient_usr_activity
+    BEFORE INSERT ON actor.usr_activity
+    FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
+
+-- given a set of activity criteria, find the most approprate activity type
+CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
+        ewho TEXT, 
+        ewhat TEXT, 
+        ehow TEXT
+    ) RETURNS SETOF config.usr_activity_type AS $$
+SELECT * FROM config.usr_activity_type 
+    WHERE 
+        enabled AND 
+        (ewho  IS NULL OR ewho  = $1) AND
+        (ewhat IS NULL OR ewhat = $2) AND
+        (ehow  IS NULL OR ehow  = $3) 
+    ORDER BY 
+        -- BOOL comparisons sort false to true
+        COALESCE(ewho, '')  != COALESCE($1, ''),
+        COALESCE(ewhat,'')  != COALESCE($2, ''),
+        COALESCE(ehow, '')  != COALESCE($3, '') 
+    LIMIT 1;
+$$ LANGUAGE SQL;
+
+-- given a set of activity criteria, finds the best
+-- activity type and inserts the activity entry
+CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
+        usr INT,
+        ewho TEXT, 
+        ewhat TEXT, 
+        ehow TEXT
+    ) RETURNS SETOF actor.usr_activity AS $$
 DECLARE
-    normalizer      RECORD;
-    value           TEXT := '';
+    new_row actor.usr_activity%ROWTYPE;
 BEGIN
+    SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
+    IF FOUND THEN
+        new_row.usr := usr;
+        INSERT INTO actor.usr_activity (usr, etype) 
+            VALUES (usr, new_row.etype)
+            RETURNING * INTO new_row;
+        RETURN NEXT new_row;
+    END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+-- SEED DATA --
+
+INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
+
+     -- authen/authz actions
+     -- note: "opensrf" is the default ingress/ehow
+     (1,  NULL, 'login',  'opensrf',      'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
+    ,(2,  NULL, 'login',  'srfsh',        'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
+    ,(3,  NULL, 'login',  'gateway-v1',   'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
+    ,(4,  NULL, 'login',  'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
+    ,(5,  NULL, 'login',  'xmlrpc',       'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
+    ,(6,  NULL, 'login',  'remoteauth',   'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
+    ,(7,  NULL, 'login',  'sip2',         'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
+    ,(8,  NULL, 'login',  'apache',       'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
+
+    ,(9,  NULL, 'verify', 'opensrf',      'authz',  oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
+    ,(10, NULL, 'verify', 'srfsh',        'authz',  oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
+    ,(11, NULL, 'verify', 'gateway-v1',   'authz',  oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
+    ,(12, NULL, 'verify', 'translator-v1','authz',  oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
+    ,(13, NULL, 'verify', 'xmlrpc',       'authz',  oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
+    ,(14, NULL, 'verify', 'remoteauth',   'authz',  oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
+    ,(15, NULL, 'verify', 'sip2',         'authz',  oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
+
+     -- authen/authz actions w/ known uses of "who"
+    ,(16, 'opac',        'login',  'gateway-v1',   'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
+    ,(17, 'opac',        'login',  'apache',       'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
+    ,(18, 'staffclient', 'login',  'gateway-v1',   'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
+    ,(19, 'selfcheck',   'login',  'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
+    ,(20, 'ums',         'login',  'xmlrpc',       'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
+    ,(21, 'authproxy',   'login',  'apache',       'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
+    ,(22, 'libraryelf',  'login',  'xmlrpc',       'authz',  oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
+
+    ,(23, 'selfcheck',   'verify', 'translator-v1','authz',  oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
+    ,(24, 'ezproxy',     'verify', 'remoteauth',   'authz',  oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
+    -- ...
+    ;
+
+-- reserve the first 1000 slots
+SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
+
+INSERT INTO config.org_unit_setting_type 
+    (name, label, description, grp, datatype) 
+    VALUES (
+        'circ.patron.usr_activity_retrieve.max',
+         oils_i18n_gettext(
+            'circ.patron.usr_activity_retrieve.max',
+            'Max user activity entries to retrieve (staff client)',
+            'coust', 
+            'label'
+        ),
+        oils_i18n_gettext(
+            'circ.patron.usr_activity_retrieve.max',
+            'Sets the maxinum number of recent user activity entries to retrieve for display in the staff client.  0 means show none, -1 means show all.  Default is 1.',
+            'coust', 
+            'description'
+        ),
+        'gui',
+        'integer'
+    );
+
+
+SELECT evergreen.upgrade_deps_block_check('0682', :eg_version);
+
+CREATE TABLE asset.copy_location_group (
+    id              SERIAL  PRIMARY KEY,
+    name            TEXT    NOT NULL, -- i18n
+    owner           INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    pos             INT     NOT NULL DEFAULT 0,
+    top             BOOL    NOT NULL DEFAULT FALSE,
+    opac_visible    BOOL    NOT NULL DEFAULT TRUE,
+    CONSTRAINT lgroup_once_per_owner UNIQUE (owner,name)
+);
+
+CREATE TABLE asset.copy_location_group_map (
+    id       SERIAL PRIMARY KEY,
+    location    INT     NOT NULL REFERENCES asset.copy_location (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    lgroup      INT     NOT NULL REFERENCES asset.copy_location_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    CONSTRAINT  lgroup_once_per_group UNIQUE (lgroup,location)
+);
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0683', :eg_version);
+
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (5, 'check_email_notify', 1);
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (7, 'check_email_notify', 1);
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (9, 'check_email_notify', 1);
+INSERT INTO action_trigger.validator (module,description) VALUES
+    ('HoldNotifyCheck',
+    oils_i18n_gettext(
+        'HoldNotifyCheck',
+        'Check Hold notification flag(s)',
+        'atval',
+        'description'
+    ));
+UPDATE action_trigger.event_definition SET validator = 'HoldNotifyCheck' WHERE id = 9;
+
+-- NOT COVERED: Adding check_sms_notify to the proper trigger. It doesn't have a static id.
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0684', :eg_version);
+
+-- schema --
+
+-- Replace the constraints with more flexible ENUM's
+ALTER TABLE vandelay.queue DROP CONSTRAINT queue_queue_type_check;
+ALTER TABLE vandelay.bib_queue DROP CONSTRAINT bib_queue_queue_type_check;
+ALTER TABLE vandelay.authority_queue DROP CONSTRAINT authority_queue_queue_type_check;
 
-    value := NEW.value;
+CREATE TYPE vandelay.bib_queue_queue_type AS ENUM ('bib', 'acq');
+CREATE TYPE vandelay.authority_queue_queue_type AS ENUM ('authority');
 
-    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;
+-- dropped column is also implemented by the child tables
+ALTER TABLE vandelay.queue DROP COLUMN queue_type; 
 
-        END LOOP;
+-- to recover after using the undo sql from below
+-- alter table vandelay.bib_queue  add column queue_type text default 'bib' not null;
+-- alter table vandelay.authority_queue  add column queue_type text default 'authority' not null;
 
-        NEW.value := value;
-    END IF;
+-- modify the child tables to use the ENUMs
+ALTER TABLE vandelay.bib_queue 
+    ALTER COLUMN queue_type DROP DEFAULT,
+    ALTER COLUMN queue_type TYPE vandelay.bib_queue_queue_type 
+        USING (queue_type::vandelay.bib_queue_queue_type),
+    ALTER COLUMN queue_type SET DEFAULT 'bib';
 
-    IF NEW.index_vector = ''::tsvector THEN
-        RETURN NEW;
-    END IF;
+ALTER TABLE vandelay.authority_queue 
+    ALTER COLUMN queue_type DROP DEFAULT,
+    ALTER COLUMN queue_type TYPE vandelay.authority_queue_queue_type 
+        USING (queue_type::vandelay.authority_queue_queue_type),
+    ALTER COLUMN queue_type SET DEFAULT 'authority';
 
-    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;
+-- give lineitems a pointer to their vandelay queued_record
 
-        END LOOP;
-    END IF;
+ALTER TABLE acq.lineitem ADD COLUMN queued_record BIGINT
+    REFERENCES vandelay.queued_bib_record (id) 
+    ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 
-    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);
-    END IF;
+ALTER TABLE acq.acq_lineitem_history ADD COLUMN queued_record BIGINT
+    REFERENCES vandelay.queued_bib_record (id) 
+    ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 
-    NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
+-- seed data --
 
-    RETURN NEW;
-END;
-$$ LANGUAGE PLPGSQL;
+INSERT INTO permission.perm_list ( id, code, description ) 
+    VALUES ( 
+        521, 
+        'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD', 
+        oils_i18n_gettext( 
+            521,
+            'Allows a user to create new bibs directly from an ACQ MARC file upload', 
+            'ppl', 
+            'description' 
+        )
+    );
 
--- Given a string such as a user might type into a search box, prepare
--- two changed variants for TO_TSQUERY(). See
--- http://www.postgresql.org/docs/9.0/static/textsearch-controls.html
--- The first variant is normalized to match indexed documents regardless
--- of diacritics.  The second variant keeps its diacritics for proper
--- highlighting via TS_HEADLINE().
-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+'), ' '
+INSERT INTO vandelay.import_error ( code, description ) 
+    VALUES ( 
+        'import.record.perm_failure', 
+        oils_i18n_gettext(
+            'import.record.perm_failure', 
+            'Perm failure creating a record', 'vie', 'description') 
     );
 
-    normalized := public.search_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;
+-- Evergreen DB patch 0685.data.bluray_vr_format.sql
+--
+-- FIXME: insert description of change, if needed
+--
 
 
--- Definition of OUT parameters changes, so must drop first
-DROP FUNCTION IF EXISTS metabib.suggest_browse_entries (TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER);
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0685', :eg_version);
 
-CREATE OR REPLACE
-    FUNCTION metabib.suggest_browse_entries(
-        raw_query_text  TEXT,   -- actually typed by humans at the UI level
-        search_class    TEXT,   -- 'alias' or 'class' or 'class|field..', etc
-        headline_opts   TEXT,   -- markup options for ts_headline()
-        visibility_org  INTEGER,-- null if you don't want opac visibility test
-        query_limit     INTEGER,-- use in LIMIT clause of interal query
-        normalization   INTEGER -- argument to TS_RANK_CD()
-    ) RETURNS TABLE (
-        value                   TEXT,   -- plain
-        field                   INTEGER,
-        buoyant_and_class_match BOOL,
-        field_match             BOOL,
-        field_weight            INTEGER,
-        rank                    REAL,
-        buoyant                 BOOL,
-        match                   TEXT    -- marked up
-    ) AS $func$
+-- FIXME: add/check SQL statements to perform the upgrade
+DO $FUNC$
 DECLARE
-    prepared_query_texts    TEXT[];
-    query                   TSQUERY;
-    plain_query             TSQUERY;
-    opac_visibility_join    TEXT;
-    search_class_join       TEXT;
-    r_fields                RECORD;
+    same_marc BOOL;
 BEGIN
-    prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
-
-    query := TO_TSQUERY('keyword', prepared_query_texts[1]);
-    plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
+    -- Check if it is already there
+    PERFORM * FROM config.marc21_physical_characteristic_value_map v
+        JOIN config.marc21_physical_characteristic_subfield_map s ON v.ptype_subfield = s.id
+        WHERE s.ptype_key = 'v' AND s.subfield = 'e' AND s.start_pos = '4' AND s.length = '1'
+            AND v.value = 's';
 
-    IF visibility_org IS NOT NULL THEN
-        opac_visibility_join := '
-    JOIN asset.opac_visible_copies aovc ON (
-        aovc.record = mbedm.source AND
-        aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
-    )';
-    ELSE
-        opac_visibility_join := '';
+    -- If it is, bail.
+    IF FOUND THEN
+        RETURN;
     END IF;
 
-    -- The following determines whether we only provide suggestsons matching
-    -- the user's selected search_class, or whether we show other suggestions
-    -- too. The reason for MIN() is that for search_classes like
-    -- 'title|proper|uniform' you would otherwise get multiple rows.  The
-    -- implication is that if title as a class doesn't have restrict,
-    -- nor does the proper field, but the uniform field does, you're going
-    -- to get 'false' for your overall evaluation of 'should we restrict?'
-    -- To invert that, change from MIN() to MAX().
+    -- Otherwise, insert it
+    INSERT INTO config.marc21_physical_characteristic_value_map (value,ptype_subfield,label)
+    SELECT 's',id,'Blu-ray'
+        FROM config.marc21_physical_characteristic_subfield_map
+        WHERE ptype_key = 'v' AND subfield = 'e' AND start_pos = '4' AND length = '1';
 
-    SELECT
-        INTO r_fields
-            MIN(cmc.restrict::INT) AS restrict_class,
-            MIN(cmf.restrict::INT) AS restrict_field
-        FROM metabib.search_class_to_registered_components(search_class)
-            AS _registered (field_class TEXT, field INT)
-        JOIN
-            config.metabib_class cmc ON (cmc.name = _registered.field_class)
-        LEFT JOIN
-            config.metabib_field cmf ON (cmf.id = _registered.field);
+    -- And reingest the blue-ray items so that things see the new value
+    SELECT INTO same_marc enabled FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc';
+    UPDATE config.internal_flag SET enabled = true WHERE name = 'ingest.reingest.force_on_same_marc';
+    UPDATE biblio.record_entry SET marc=marc WHERE id IN (SELECT record
+        FROM
+            metabib.full_rec a JOIN metabib.full_rec b USING (record)
+        WHERE
+            a.tag = 'LDR' AND a.value LIKE '______g%'
+        AND b.tag = '007' AND b.value LIKE 'v___s%');
+    UPDATE config.internal_flag SET enabled = same_marc WHERE name = 'ingest.reingest.force_on_same_marc';
+END;
+$FUNC$;
 
-    -- evaluate 'should we restrict?'
-    IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
-        search_class_join := '
-    JOIN
-        metabib.search_class_to_registered_components($2)
-        AS _registered (field_class TEXT, field INT) ON (
-            (_registered.field IS NULL AND
-                _registered.field_class = cmf.field_class) OR
-            (_registered.field = cmf.id)
-        )
-    ';
-    ELSE
-        search_class_join := '
-    LEFT JOIN
-        metabib.search_class_to_registered_components($2)
-        AS _registered (field_class TEXT, field INT) ON (
-            _registered.field_class = cmc.name
-        )
-    ';
-    END IF;
 
-    RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $7, $3) FROM (SELECT DISTINCT
-        mbe.value,
-        cmf.id,
-        cmc.buoyant AND _registered.field_class IS NOT NULL,
-        _registered.field = cmf.id,
-        cmf.weight,
-        TS_RANK_CD(mbe.index_vector, $1, $6),
-        cmc.buoyant
-    FROM metabib.browse_entry_def_map mbedm
-    JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
-    JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
-    JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
-    '  || search_class_join || opac_visibility_join ||
-    ' WHERE $1 @@ mbe.index_vector
-    ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
-    LIMIT $5) x
-    ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
-    '   -- sic, repeat the order by clause in the outer select too
-    USING
-        query, search_class, headline_opts,
-        visibility_org, query_limit, normalization, plain_query
-        ;
+-- Evergreen DB patch 0686.schema.auditor_boost.sql
+--
+-- FIXME: insert description of change, if needed
+--
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0686', :eg_version);
+
+-- FIXME: add/check SQL statements to perform the upgrade
+-- These three functions are for capturing, getting, and clearing user and workstation information
+
+-- Set the User AND workstation in one call. Tis faster. And less calls.
+-- First argument is user, second is workstation
+CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
+    $_SHARED{"eg_audit_user"} = $_[0];
+    $_SHARED{"eg_audit_ws"} = $_[1];
+$$ LANGUAGE plperl;
+
+-- Get the User AND workstation in one call. Less calls, useful for joins ;)
+CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
+    return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
+$$ LANGUAGE plperl;
 
-    -- sort order:
-    --  buoyant AND chosen class = match class
-    --  chosen field = match field
-    --  field weight
-    --  rank
-    --  buoyancy
-    --  value itself
+-- Clear the audit info, for whatever reason
+CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
+    delete($_SHARED{"eg_audit_user"});
+    delete($_SHARED{"eg_audit_ws"});
+$$ LANGUAGE plperl;
 
+CREATE OR REPLACE FUNCTION auditor.create_auditor_history ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
+BEGIN
+    EXECUTE $$
+        CREATE TABLE auditor.$$ || sch || $$_$$ || tbl || $$_history (
+            audit_id	BIGINT				PRIMARY KEY,
+            audit_time	TIMESTAMP WITH TIME ZONE	NOT NULL,
+            audit_action	TEXT				NOT NULL,
+            audit_user  INT,
+            audit_ws    INT,
+            LIKE $$ || sch || $$.$$ || tbl || $$
+        );
+    $$;
+	RETURN TRUE;
 END;
-$func$ LANGUAGE PLPGSQL;
+$creator$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION auditor.create_auditor_func    ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
+DECLARE
+    column_list TEXT[];
+BEGIN
+    SELECT INTO column_list array_agg(a.attname)
+        FROM pg_catalog.pg_attribute a
+            JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
+            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+        WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
 
+    EXECUTE $$
+        CREATE OR REPLACE FUNCTION auditor.audit_$$ || sch || $$_$$ || tbl || $$_func ()
+        RETURNS TRIGGER AS $func$
+        BEGIN
+            INSERT INTO auditor.$$ || sch || $$_$$ || tbl || $$_history ( audit_id, audit_time, audit_action, audit_user, audit_ws, $$
+            || array_to_string(column_list, ', ') || $$ )
+                SELECT  nextval('auditor.$$ || sch || $$_$$ || tbl || $$_pkey_seq'),
+                    now(),
+                    SUBSTR(TG_OP,1,1),
+                    eg_user,
+                    eg_ws,
+                    OLD.$$ || array_to_string(column_list, ', OLD.') || $$
+                FROM auditor.get_audit_info();
+            RETURN NULL;
+        END;
+        $func$ LANGUAGE 'plpgsql';
+    $$;
+    RETURN TRUE;
+END;
+$creator$ LANGUAGE 'plpgsql';
 
-\qecho 
-\qecho The following takes about a minute per 100,000 rows in
-\qecho metabib.browse_entry on my development system, which is only a VM with
-\qecho 4 GB of memory and 2 cores.
-\qecho 
-\qecho The following is a very loose estimate of how long the next UPDATE
-\qecho statement would take to finish on MY machine, based on YOUR number
-\qecho of rows in metabib.browse_entry:
-\qecho 
+CREATE OR REPLACE FUNCTION auditor.create_auditor_lifecycle     ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
+DECLARE
+    column_list TEXT[];
+BEGIN
+    SELECT INTO column_list array_agg(a.attname)
+        FROM pg_catalog.pg_attribute a
+            JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
+            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+        WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
 
-SELECT (COUNT(id) / 100000.0) * INTERVAL '1 minute'
-    AS "approximate duration of following UPDATE statement"
-    FROM metabib.browse_entry;
+    EXECUTE $$
+        CREATE VIEW auditor.$$ || sch || $$_$$ || tbl || $$_lifecycle AS
+            SELECT -1 AS audit_id,
+                   now() AS audit_time,
+                   '-' AS audit_action,
+                   -1 AS audit_user,
+                   -1 AS audit_ws,
+                   $$ || array_to_string(column_list, ', ') || $$
+              FROM $$ || sch || $$.$$ || tbl || $$
+                UNION ALL
+            SELECT audit_id, audit_time, audit_action, audit_user, audit_ws,
+            $$ || array_to_string(column_list, ', ') || $$
+              FROM auditor.$$ || sch || $$_$$ || tbl || $$_history;
+    $$;
+    RETURN TRUE;
+END;
+$creator$ LANGUAGE 'plpgsql';
+
+-- Corrects all column discrepencies between audit table and core table:
+-- Adds missing columns
+-- Removes leftover columns
+-- Updates types
+-- Also, ensures all core auditor columns exist.
+CREATE OR REPLACE FUNCTION auditor.fix_columns() RETURNS VOID AS $BODY$
+DECLARE
+    current_table TEXT = ''; -- Storage for post-loop main table name
+    current_audit_table TEXT = ''; -- Storage for post-loop audit table name
+    query TEXT = ''; -- Storage for built query
+    cr RECORD; -- column record object
+    alter_t BOOL = false; -- Has the alter table command been appended yet
+    auditor_cores TEXT[] = ARRAY[]::TEXT[]; -- Core auditor function list (filled inside of loop)
+    core_column TEXT; -- The current core column we are adding
+BEGIN
+    FOR cr IN
+        WITH audit_tables AS ( -- Basic grab of auditor tables. Anything in the auditor namespace, basically. With oids.
+            SELECT c.oid AS audit_oid, c.relname AS audit_table
+            FROM pg_catalog.pg_class c
+            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+            WHERE relkind='r' AND nspname = 'auditor'
+        ),
+        table_set AS ( -- Union of auditor tables with their "main" tables. With oids.
+            SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
+            FROM pg_catalog.pg_class c
+            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+            JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
+            WHERE relkind = 'r'
+        ),
+        column_lists AS ( -- All columns associated with the auditor or main table, grouped by the main table's oid.
+            SELECT DISTINCT ON (main_oid, attname) t.main_oid, a.attname
+            FROM table_set t
+            JOIN pg_catalog.pg_attribute a ON a.attrelid IN (t.main_oid, t.audit_oid)
+            WHERE attnum > 0 AND NOT attisdropped
+        ),
+        column_defs AS ( -- The motherload, every audit table and main table plus column names and defs.
+            SELECT audit_table,
+                   main_namespace,
+                   main_table,
+                   a.attname AS main_column, -- These two will be null for columns that have since been deleted, or for auditor core columns
+                   pg_catalog.format_type(a.atttypid, a.atttypmod) AS main_column_def,
+                   b.attname AS audit_column, -- These two will be null for columns that have since been added
+                   pg_catalog.format_type(b.atttypid, b.atttypmod) AS audit_column_def
+            FROM table_set t
+            JOIN column_lists c USING (main_oid)
+            LEFT JOIN pg_catalog.pg_attribute a ON a.attname = c.attname AND a.attrelid = t.main_oid AND a.attnum > 0 AND NOT a.attisdropped
+            LEFT JOIN pg_catalog.pg_attribute b ON b.attname = c.attname AND b.attrelid = t.audit_oid AND b.attnum > 0 AND NOT b.attisdropped
+        )
+        -- Nice sorted output from the above
+        SELECT * FROM column_defs WHERE main_column_def IS DISTINCT FROM audit_column_def ORDER BY main_namespace, main_table, main_column, audit_column
+    LOOP
+        IF current_table <> (cr.main_namespace || '.' || cr.main_table) THEN -- New table?
+            FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Update missing core auditor columns
+                IF NOT alter_t THEN -- Add ALTER TABLE if we haven't already
+                    query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
+                    alter_t:=TRUE;
+                ELSE
+                    query:=query || $$,$$;
+                END IF;
+                -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
+                query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
+            END LOOP;
+            IF alter_t THEN -- Open alter table = needs a semicolon
+                query:=query || $$; $$;
+                alter_t:=FALSE;
+                IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
+                    -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
+                    -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
+                    EXECUTE query;
+                    EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
+                        $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
+                    EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
+                        $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
+                    query:='';
+                END IF;
+            END IF;
+            -- New table means we reset the list of needed auditor core columns
+            auditor_cores = ARRAY['audit_id bigint', 'audit_time timestamp with time zone', 'audit_action text', 'audit_user integer', 'audit_ws integer'];
+            -- And store some values for use later, because we can't rely on cr in all places.
+            current_table:=cr.main_namespace || '.' || cr.main_table;
+            current_audit_table:=cr.audit_table;
+        END IF;
+        IF cr.main_column IS NULL AND cr.audit_column LIKE 'audit_%' THEN -- Core auditor column?
+            -- Remove core from list of cores
+            SELECT INTO auditor_cores array_agg(core) FROM unnest(auditor_cores) AS core WHERE core != (cr.audit_column || ' ' || cr.audit_column_def);
+        ELSIF cr.main_column IS NULL THEN -- Main column doesn't exist, and it isn't an auditor column. Needs dropping from the auditor.
+            IF NOT alter_t THEN
+                query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
+                alter_t:=TRUE;
+            ELSE
+                query:=query || $$,$$;
+            END IF;
+            query:=query || $$ DROP COLUMN $$ || cr.audit_column;
+        ELSIF cr.audit_column IS NULL AND cr.main_column IS NOT NULL THEN -- New column auditor doesn't have. Add it.
+            IF NOT alter_t THEN
+                query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
+                alter_t:=TRUE;
+            ELSE
+                query:=query || $$,$$;
+            END IF;
+            query:=query || $$ ADD COLUMN $$ || cr.main_column || $$ $$ || cr.main_column_def;
+        ELSIF cr.main_column IS NOT NULL AND cr.audit_column IS NOT NULL THEN -- Both sides have this column, but types differ. Fix that.
+            IF NOT alter_t THEN
+                query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
+                alter_t:=TRUE;
+            ELSE
+                query:=query || $$,$$;
+            END IF;
+            query:=query || $$ ALTER COLUMN $$ || cr.audit_column || $$ TYPE $$ || cr.main_column_def;
+        END IF;
+    END LOOP;
+    FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Repeat this outside of the loop to catch the last table
+        IF NOT alter_t THEN
+            query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
+            alter_t:=TRUE;
+        ELSE
+            query:=query || $$,$$;
+        END IF;
+        -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
+        query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
+    END LOOP;
+    IF alter_t THEN -- Open alter table = needs a semicolon
+        query:=query || $$;$$;
+        IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
+            -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
+            -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
+            EXECUTE query;
+            EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
+                $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
+            EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
+                $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
+            query:='';
+        END IF;
+    END IF;
+    EXECUTE query;
+END;
+$BODY$ LANGUAGE plpgsql;
 
-UPDATE metabib.browse_entry SET index_vector = TO_TSVECTOR(
-    'keyword',
-    public.search_normalize(
-        ARRAY_TO_STRING(
-            evergreen.regexp_split_to_array(value, E'\\W+'), ' '
+-- Update it all routine
+CREATE OR REPLACE FUNCTION auditor.update_auditors() RETURNS boolean AS $BODY$
+DECLARE
+    auditor_name TEXT;
+    table_schema TEXT;
+    table_name TEXT;
+BEGIN
+    -- Drop Lifecycle view(s) before potential column changes
+    FOR auditor_name IN
+        SELECT c.relname
+            FROM pg_catalog.pg_class c
+                JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+            WHERE relkind = 'v' AND n.nspname = 'auditor' LOOP
+        EXECUTE $$ DROP VIEW auditor.$$ || auditor_name || $$;$$;
+    END LOOP;
+    -- Fix all column discrepencies
+    PERFORM auditor.fix_columns();
+    -- Re-create trigger functions and lifecycle views
+    FOR table_schema, table_name IN
+        WITH audit_tables AS (
+            SELECT c.oid AS audit_oid, c.relname AS audit_table
+            FROM pg_catalog.pg_class c
+            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+            WHERE relkind='r' AND nspname = 'auditor'
+        ),
+        table_set AS (
+            SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
+            FROM pg_catalog.pg_class c
+            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+            JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
+            WHERE relkind = 'r'
         )
-    )
-);
-
-
-SELECT evergreen.upgrade_deps_block_check('0680', :eg_version);
-
--- Not much use in having identifier-class fields be suggestions. Credit for the idea goes to Ben Shum.
-UPDATE config.metabib_field SET browse_field = FALSE WHERE id < 100 AND field_class = 'identifier';
-
+        SELECT main_namespace, main_table FROM table_set LOOP
+        
+        PERFORM auditor.create_auditor_func(table_schema, table_name);
+        PERFORM auditor.create_auditor_lifecycle(table_schema, table_name);
+    END LOOP;
+    RETURN TRUE;
+END;
+$BODY$ LANGUAGE plpgsql;
 
----------------------------------------------------------------------------
--- The rest of this was tested on Evergreen Indiana's dev server, which has
--- a large data set  of 2.6M bibs, and was instrumental in sussing out the
--- needed adjustments.  Thanks, EG-IN!
----------------------------------------------------------------------------
+-- Go ahead and update them all now
+SELECT auditor.update_auditors();
 
--- GIN indexes are /much/ better for prefix matching, which is important for browse and autosuggest
---Commented out the creation earlier, so we don't need to drop it here.
---DROP INDEX metabib.metabib_browse_entry_index_vector_idx;
-CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIN (index_vector);
 
+-- Evergreen DB patch 0687.schema.enhance_reingest.sql
+--
+-- FIXME: insert description of change, if needed
+--
 
--- We need thes to make the autosuggest limiting joins fast
-CREATE INDEX browse_entry_def_map_def_idx ON metabib.browse_entry_def_map (def);
-CREATE INDEX browse_entry_def_map_entry_idx ON metabib.browse_entry_def_map (entry);
-CREATE INDEX browse_entry_def_map_source_idx ON metabib.browse_entry_def_map (source);
 
--- In practice this will always be ~1 row, and the default of 1000 causes terrible plans
-ALTER FUNCTION metabib.search_class_to_registered_components(text) ROWS 1;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0687', :eg_version);
 
--- Reworking of the generated query to act in a sane manner in the face of large datasets
-CREATE OR REPLACE
-    FUNCTION metabib.suggest_browse_entries(
-        raw_query_text  TEXT,   -- actually typed by humans at the UI level
-        search_class    TEXT,   -- 'alias' or 'class' or 'class|field..', etc
-        headline_opts   TEXT,   -- markup options for ts_headline()
-        visibility_org  INTEGER,-- null if you don't want opac visibility test
-        query_limit     INTEGER,-- use in LIMIT clause of interal query
-        normalization   INTEGER -- argument to TS_RANK_CD()
-    ) RETURNS TABLE (
-        value                   TEXT,   -- plain
-        field                   INTEGER,
-        buoyant_and_class_match BOOL,
-        field_match             BOOL,
-        field_weight            INTEGER,
-        rank                    REAL,
-        buoyant                 BOOL,
-        match                   TEXT    -- marked up
-    ) AS $func$
+-- FIXME: add/check SQL statements to perform the upgrade
+-- New function def
+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
-    prepared_query_texts    TEXT[];
-    query                   TSQUERY;
-    plain_query             TSQUERY;
-    opac_visibility_join    TEXT;
-    search_class_join       TEXT;
-    r_fields                RECORD;
+    fclass          RECORD;
+    ind_data        metabib.field_entry_template%ROWTYPE;
+    mbe_row         metabib.browse_entry%ROWTYPE;
+    mbe_id          BIGINT;
 BEGIN
-    prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
-
-    query := TO_TSQUERY('keyword', prepared_query_texts[1]);
-    plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
-
-    IF visibility_org IS NOT NULL THEN
-        opac_visibility_join := '
-    JOIN asset.opac_visible_copies aovc ON (
-        aovc.record = x.source AND
-        aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
-    )';
-    ELSE
-        opac_visibility_join := '';
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+    IF NOT FOUND THEN
+        IF NOT 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 skip_facet THEN
+            DELETE FROM metabib.facet_entry WHERE source = bib_id;
+        END IF;
+        IF NOT skip_browse THEN
+            DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
+        END IF;
     END IF;
 
-    -- The following determines whether we only provide suggestsons matching
-    -- the user's selected search_class, or whether we show other suggestions
-    -- too. The reason for MIN() is that for search_classes like
-    -- 'title|proper|uniform' you would otherwise get multiple rows.  The
-    -- implication is that if title as a class doesn't have restrict,
-    -- nor does the proper field, but the uniform field does, you're going
-    -- to get 'false' for your overall evaluation of 'should we restrict?'
-    -- To invert that, change from MIN() to MAX().
-
-    SELECT
-        INTO r_fields
-            MIN(cmc.restrict::INT) AS restrict_class,
-            MIN(cmf.restrict::INT) AS restrict_field
-        FROM metabib.search_class_to_registered_components(search_class)
-            AS _registered (field_class TEXT, field INT)
-        JOIN
-            config.metabib_class cmc ON (cmc.name = _registered.field_class)
-        LEFT JOIN
-            config.metabib_field cmf ON (cmf.id = _registered.field);
-
-    -- evaluate 'should we restrict?'
-    IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
-        search_class_join := '
-    JOIN
-        metabib.search_class_to_registered_components($2)
-        AS _registered (field_class TEXT, field INT) ON (
-            (_registered.field IS NULL AND
-                _registered.field_class = cmf.field_class) OR
-            (_registered.field = cmf.id)
-        )
-    ';
-    ELSE
-        search_class_join := '
-    LEFT JOIN
-        metabib.search_class_to_registered_components($2)
-        AS _registered (field_class TEXT, field INT) ON (
-            _registered.field_class = cmc.name
-        )
-    ';
-    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;
 
-    RETURN QUERY EXECUTE '
-SELECT  DISTINCT
-        x.value,
-        x.id,
-        x.push,
-        x.restrict,
-        x.weight,
-        x.ts_rank_cd,
-        x.buoyant,
-        TS_HEADLINE(value, $7, $3)
-  FROM  (SELECT DISTINCT
-                mbe.value,
-                cmf.id,
-                cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
-                _registered.field = cmf.id AS restrict,
-                cmf.weight,
-                TS_RANK_CD(mbe.index_vector, $1, $6),
-                cmc.buoyant,
-                mbedm.source
-          FROM  metabib.browse_entry_def_map mbedm
+        IF ind_data.facet_field AND NOT skip_facet THEN
+            INSERT INTO metabib.facet_entry (field, source, value)
+                VALUES (ind_data.field, ind_data.source, ind_data.value);
+        END IF;
 
-                -- Start with a pre-limited set of 10k possible suggestions. More than that is not going to be useful anyway
-                JOIN (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000) mbe ON (mbe.id = mbedm.entry)
+        IF ind_data.browse_field AND NOT 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;
 
-                JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
-                JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
-                '  || search_class_join || '
-          ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
-          LIMIT 1000) AS x -- This outer limit makes testing for opac visibility usably fast
-        ' || opac_visibility_join || '
-  ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
-  LIMIT $5
-'   -- sic, repeat the order by clause in the outer select too
-    USING
-        query, search_class, headline_opts,
-        visibility_org, query_limit, normalization, plain_query
-        ;
+            INSERT INTO metabib.browse_entry_def_map (entry, def, source)
+                VALUES (mbe_id, ind_data.field, ind_data.source);
+        END IF;
 
-    -- sort order:
-    --  buoyant AND chosen class = match class
-    --  chosen field = match field
-    --  field weight
-    --  rank
-    --  buoyancy
-    --  value itself
+        IF ind_data.search_field AND NOT 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;
 
+    RETURN;
 END;
 $func$ LANGUAGE PLPGSQL;
 
+-- Delete old one
+DROP FUNCTION IF EXISTS metabib.reingest_metabib_field_entries(BIGINT);
 
--- Evergreen DB patch 0681.schema.user-activity.sql
+-- Evergreen DB patch 0688.data.circ_history_export_csv.sql
+--
+-- FIXME: insert description of change, if needed
 --
 
 -- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0681', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('0688', :eg_version);
 
--- SCHEMA --
+INSERT INTO action_trigger.hook (key, core_type, description, passive)
+VALUES (
+    'circ.format.history.csv',
+    'circ',
+    oils_i18n_gettext(
+        'circ.format.history.csv',
+        'Produce CSV of circulation history',
+        'ath',
+        'description'
+    ),
+    FALSE
+);
 
-CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
+INSERT INTO action_trigger.event_definition (
+    active, owner, name, hook, reactor, validator, group_field, template) 
+VALUES (
+    TRUE, 1, 'Circ History CSV', 'circ.format.history.csv', 'ProcessTemplate', 'NOOP_True', 'usr',
+$$
+Title,Author,Call Number,Barcode,Format
+[%-
+FOR circ IN target;
+    bibxml = helpers.unapi_bre(circ.target_copy.call_number.record, {flesh => '{mra}'});
+    title = "";
+    FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
+        title = title _ part.textContent;
+    END;
+    author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
+    item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value') %]
 
-CREATE TABLE config.usr_activity_type (
-    id          SERIAL                      PRIMARY KEY, 
-    ewho        TEXT,
-    ewhat       TEXT,
-    ehow        TEXT,
-    label       TEXT                        NOT NULL, -- i18n
-    egroup      config.usr_activity_group   NOT NULL,
-    enabled     BOOL                        NOT NULL DEFAULT TRUE,
-    transient   BOOL                        NOT NULL DEFAULT FALSE,
-    CONSTRAINT  one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
+    [%- helpers.csv_datum(title) -%],
+    [%- helpers.csv_datum(author) -%],
+    [%- helpers.csv_datum(circ.target_copy.call_number.label) -%],
+    [%- helpers.csv_datum(circ.target_copy.barcode) -%],
+    [%- helpers.csv_datum(item_type) %]
+[%- END -%]
+$$
 );
 
-CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type 
-    (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
+INSERT INTO action_trigger.environment (event_def, path)
+    VALUES (
+        currval('action_trigger.event_definition_id_seq'),
+        'target_copy.call_number'
+    );
 
-CREATE TABLE actor.usr_activity (
-    id          BIGSERIAL   PRIMARY KEY,
-    usr         INT         REFERENCES actor.usr (id) ON DELETE SET NULL,
-    etype       INT         NOT NULL REFERENCES config.usr_activity_type (id),
-    event_time  TIMESTAMPTZ NOT NULL DEFAULT NOW()
-);
 
--- remove transient activity entries on insert of new entries
-CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
-BEGIN
-    DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
-        WHERE atype.transient AND 
-            NEW.etype = atype.id AND
-            act.etype = atype.id AND
-            act.usr = NEW.usr;
-    RETURN NEW;
-END;
-$$ LANGUAGE PLPGSQL;
+-- Evergreen DB patch 0689.data.record_print_format_update.sql
+--
+-- Updates print and email templates for bib record actions
+--
 
-CREATE TRIGGER remove_transient_usr_activity
-    BEFORE INSERT ON actor.usr_activity
-    FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0689', :eg_version);
 
--- given a set of activity criteria, find the most approprate activity type
-CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
-        ewho TEXT, 
-        ewhat TEXT, 
-        ehow TEXT
-    ) RETURNS SETOF config.usr_activity_type AS $$
-SELECT * FROM config.usr_activity_type 
-    WHERE 
-        enabled AND 
-        (ewho  IS NULL OR ewho  = $1) AND
-        (ewhat IS NULL OR ewhat = $2) AND
-        (ehow  IS NULL OR ehow  = $3) 
-    ORDER BY 
-        -- BOOL comparisons sort false to true
-        COALESCE(ewho, '')  != COALESCE($1, ''),
-        COALESCE(ewhat,'')  != COALESCE($2, ''),
-        COALESCE(ehow, '')  != COALESCE($3, '') 
-    LIMIT 1;
-$$ LANGUAGE SQL;
+UPDATE action_trigger.event_definition SET template = $$
+<div>
+    <style> li { padding: 8px; margin 5px; }</style>
+    <ol>
+    [% FOR cbreb IN target %]
+    [% FOR item IN cbreb.items;
+        bre_id = item.target_biblio_record_entry;
 
--- given a set of activity criteria, finds the best
--- activity type and inserts the activity entry
-CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
-        usr INT,
-        ewho TEXT, 
-        ewhat TEXT, 
-        ehow TEXT
-    ) RETURNS SETOF actor.usr_activity AS $$
-DECLARE
-    new_row actor.usr_activity%ROWTYPE;
-BEGIN
-    SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
-    IF FOUND THEN
-        new_row.usr := usr;
-        INSERT INTO actor.usr_activity (usr, etype) 
-            VALUES (usr, new_row.etype)
-            RETURNING * INTO new_row;
-        RETURN NEXT new_row;
-    END IF;
-END;
-$$ LANGUAGE plpgsql;
+        bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
+        FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
+            title = title _ part.textContent;
+        END;
 
--- SEED DATA --
+        author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
+        item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
+        publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
+        pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
+        isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
+        issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
+        upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
+        %]
 
-INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
+        <li>
+            Bib ID# [% bre_id %]<br/>
+            [% IF isbn %]ISBN: [% isbn %]<br/>[% END %]
+            [% IF issn %]ISSN: [% issn %]<br/>[% END %]
+            [% IF upc  %]UPC:  [% upc %]<br/>[% END %]
+            Title: [% title %]<br />
+            Author: [% author %]<br />
+            Publication Info: [% publisher %] [% pubdate %]<br/>
+            Item Type: [% item_type %]
+        </li>
+    [% END %]
+    [% END %]
+    </ol>
+</div>
+$$ 
+WHERE hook = 'biblio.format.record_entry.print' AND id < 100; -- sample data
 
-     -- authen/authz actions
-     -- note: "opensrf" is the default ingress/ehow
-     (1,  NULL, 'login',  'opensrf',      'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
-    ,(2,  NULL, 'login',  'srfsh',        'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
-    ,(3,  NULL, 'login',  'gateway-v1',   'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
-    ,(4,  NULL, 'login',  'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
-    ,(5,  NULL, 'login',  'xmlrpc',       'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
-    ,(6,  NULL, 'login',  'remoteauth',   'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
-    ,(7,  NULL, 'login',  'sip2',         'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
-    ,(8,  NULL, 'login',  'apache',       'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
 
-    ,(9,  NULL, 'verify', 'opensrf',      'authz',  oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
-    ,(10, NULL, 'verify', 'srfsh',        'authz',  oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
-    ,(11, NULL, 'verify', 'gateway-v1',   'authz',  oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
-    ,(12, NULL, 'verify', 'translator-v1','authz',  oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
-    ,(13, NULL, 'verify', 'xmlrpc',       'authz',  oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
-    ,(14, NULL, 'verify', 'remoteauth',   'authz',  oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
-    ,(15, NULL, 'verify', 'sip2',         'authz',  oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
+UPDATE action_trigger.event_definition SET delay = '00:00:00', template = $$
+[%- SET user = target.0.owner -%]
+To: [%- params.recipient_email || user.email %]
+From: [%- params.sender_email || default_sender %]
+Subject: Bibliographic Records
+
+[% FOR cbreb IN target %]
+[% FOR item IN cbreb.items;
+    bre_id = item.target_biblio_record_entry;
+
+    bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
+    FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
+        title = title _ part.textContent;
+    END;
+
+    author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
+    item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
+    publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
+    pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
+    isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
+    issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
+    upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
+%]
+
+[% loop.count %]/[% loop.size %].  Bib ID# [% bre_id %] 
+[% IF isbn %]ISBN: [% isbn _ "\n" %][% END -%]
+[% IF issn %]ISSN: [% issn _ "\n" %][% END -%]
+[% IF upc  %]UPC:  [% upc _ "\n" %] [% END -%]
+Title: [% title %]
+Author: [% author %]
+Publication Info: [% publisher %] [% pubdate %]
+Item Type: [% item_type %]
+
+[% END %]
+[% END %]
+$$ 
+WHERE hook = 'biblio.format.record_entry.email' AND id < 100; -- sample data
+
+-- remove a swath of unused environment entries
+
+DELETE FROM action_trigger.environment env 
+    USING action_trigger.event_definition def 
+    WHERE env.event_def = def.id AND 
+        env.path != 'items' AND 
+        def.hook = 'biblio.format.record_entry.print' AND 
+        def.id < 100; -- sample data
+
+DELETE FROM action_trigger.environment env 
+    USING action_trigger.event_definition def 
+    WHERE env.event_def = def.id AND 
+        env.path != 'items' AND 
+        env.path != 'owner' AND 
+        def.hook = 'biblio.format.record_entry.email' AND 
+        def.id < 100; -- sample data
+
+-- Evergreen DB patch 0690.schema.unapi_limit_rank.sql
+--
+-- Rewrite the in-database unapi functions to include per-object limits and
+-- offsets, such as a maximum number of copies and call numbers for given
+-- bib record via the HSTORE syntax (for example, 'acn => 5, acp => 10' would
+-- limit to a maximum of 5 call numbers for the bib, with up to 10 copies per
+-- call number).
+--
+-- Add some notion of "preferred library" that will provide copy counts
+-- and optionally affect the sorting of returned copies.
+--
+-- Sort copies by availability, preferring the most available copies.
+--
+-- Return located URIs.
+--
+--
 
-     -- authen/authz actions w/ known uses of "who"
-    ,(16, 'opac',        'login',  'gateway-v1',   'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
-    ,(17, 'opac',        'login',  'apache',       'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
-    ,(18, 'staffclient', 'login',  'gateway-v1',   'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
-    ,(19, 'selfcheck',   'login',  'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
-    ,(20, 'ums',         'login',  'xmlrpc',       'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
-    ,(21, 'authproxy',   'login',  'apache',       'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
-    ,(22, 'libraryelf',  'login',  'xmlrpc',       'authz',  oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0690', :eg_version);
 
-    ,(23, 'selfcheck',   'verify', 'translator-v1','authz',  oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
-    ,(24, 'ezproxy',     'verify', 'remoteauth',   'authz',  oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
-    -- ...
-    ;
+-- The simplest way to apply all of these changes is just to replace the unapi
+-- schema entirely -- the following is a copy of 990.schema.unapi.sql with
+-- the initial COMMIT in place in case the upgrade_deps_block_check fails;
+-- if it does, then the attempt to create the unapi schema in the following
+-- transaction will also fail. Not graceful, but safe!
+DROP SCHEMA IF EXISTS unapi CASCADE;
 
--- reserve the first 1000 slots
-SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
+CREATE SCHEMA unapi;
 
-INSERT INTO config.org_unit_setting_type 
-    (name, label, description, grp, datatype) 
-    VALUES (
-        'circ.patron.usr_activity_retrieve.max',
-         oils_i18n_gettext(
-            'circ.patron.usr_activity_retrieve.max',
-            'Max user activity entries to retrieve (staff client)',
-            'coust', 
-            'label'
-        ),
-        oils_i18n_gettext(
-            'circ.patron.usr_activity_retrieve.max',
-            'Sets the maxinum number of recent user activity entries to retrieve for display in the staff client.  0 means show none, -1 means show all.  Default is 1.',
-            'coust', 
-            'description'
+CREATE OR REPLACE FUNCTION evergreen.org_top()
+RETURNS SETOF actor.org_unit AS $$
+    SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1;
+$$ LANGUAGE SQL STABLE
+ROWS 1;
+
+CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
+RETURNS anyarray AS $$
+    SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
+$$ LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION evergreen.rank_ou(lib INT, search_lib INT, pref_lib INT DEFAULT NULL)
+RETURNS INTEGER AS $$
+    WITH search_libs AS (
+        SELECT id, distance FROM actor.org_unit_descendants_distance($2)
+    )
+    SELECT COALESCE(
+        (SELECT -10000 FROM actor.org_unit
+         WHERE $1 = $3 AND id = $3 AND $2 IN (
+                SELECT id FROM actor.org_unit WHERE parent_ou IS NULL
+             )
         ),
-        'gui',
-        'integer'
+        (SELECT distance FROM search_libs WHERE id = $1),
+        10000
     );
+$$ LANGUAGE SQL STABLE;
 
+CREATE OR REPLACE FUNCTION evergreen.rank_cp_status(status INT)
+RETURNS INTEGER AS $$
+    WITH totally_available AS (
+        SELECT id, 0 AS avail_rank
+        FROM config.copy_status
+        WHERE opac_visible IS TRUE
+            AND copy_active IS TRUE
+            AND id != 1 -- "Checked out"
+    ), almost_available AS (
+        SELECT id, 10 AS avail_rank
+        FROM config.copy_status
+        WHERE holdable IS TRUE
+            AND opac_visible IS TRUE
+            AND copy_active IS FALSE
+            OR id = 1 -- "Checked out"
+    )
+    SELECT COALESCE(
+        (SELECT avail_rank FROM totally_available WHERE $1 IN (id)),
+        (SELECT avail_rank FROM almost_available WHERE $1 IN (id)),
+        100
+    );
+$$ LANGUAGE SQL STABLE;
 
-SELECT evergreen.upgrade_deps_block_check('0682', :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
+) 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 = $1
+            AND acn.deleted IS FALSE
+            AND acp.deleted IS FALSE
+        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 TABLE asset.copy_location_group (
-    id              SERIAL  PRIMARY KEY,
-    name            TEXT    NOT NULL, -- i18n
-    owner           INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    pos             INT     NOT NULL DEFAULT 0,
-    top             BOOL    NOT NULL DEFAULT FALSE,
-    opac_visible    BOOL    NOT NULL DEFAULT TRUE,
-    CONSTRAINT lgroup_once_per_owner UNIQUE (owner,name)
-);
+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 acn.id, aou.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
+           INNER JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
+      WHERE acn.record = $1
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE
+    UNION
+    SELECT acn.id, aou.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
+           INNER JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
+      WHERE acn.record = $1
+          AND acn.deleted IS FALSE
+          AND auri.active IS TRUE;
+$$
+LANGUAGE SQL STABLE;
 
-CREATE TABLE asset.copy_location_group_map (
-    id       SERIAL PRIMARY KEY,
-    location    INT     NOT NULL REFERENCES asset.copy_location (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    lgroup      INT     NOT NULL REFERENCES asset.copy_location_group (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    CONSTRAINT  lgroup_once_per_group UNIQUE (lgroup,location)
+CREATE TABLE unapi.bre_output_layout (
+    name                TEXT    PRIMARY KEY,
+    transform           TEXT    REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    mime_type           TEXT    NOT NULL,
+    feed_top            TEXT    NOT NULL,
+    holdings_element    TEXT,
+    title_element       TEXT,
+    description_element TEXT,
+    creator_element     TEXT,
+    update_ts_element   TEXT
 );
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0683', :eg_version);
+INSERT INTO unapi.bre_output_layout
+    (name,           transform, mime_type,              holdings_element, feed_top,         title_element, description_element, creator_element, update_ts_element)
+        VALUES
+    ('holdings_xml', NULL,      'application/xml',      NULL,             'hxml',           NULL,          NULL,                NULL,            NULL),
+    ('marcxml',      'marcxml', 'application/marc+xml', 'record',         'collection',     NULL,          NULL,                NULL,            NULL),
+    ('mods32',       'mods32',  'application/mods+xml', 'mods',           'modsCollection', NULL,          NULL,                NULL,            NULL)
+;
 
-INSERT INTO action_trigger.event_params (event_def, param, value)
-    VALUES (5, 'check_email_notify', 1);
-INSERT INTO action_trigger.event_params (event_def, param, value)
-    VALUES (7, 'check_email_notify', 1);
-INSERT INTO action_trigger.event_params (event_def, param, value)
-    VALUES (9, 'check_email_notify', 1);
-INSERT INTO action_trigger.validator (module,description) VALUES
-    ('HoldNotifyCheck',
-    oils_i18n_gettext(
-        'HoldNotifyCheck',
-        'Check Hold notification flag(s)',
-        'atval',
-        'description'
-    ));
-UPDATE action_trigger.event_definition SET validator = 'HoldNotifyCheck' WHERE id = 9;
+-- Dummy functions, so we can create the real ones out of order
+CREATE OR REPLACE FUNCTION unapi.aou    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.acnp   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.acns   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.acn    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.ssub   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.sdist  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.sstr   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.sitem  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.sunit  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.sisum  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.sbsum  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.sssum  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.siss   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.auri   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.acp    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.acpn   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.acl    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.ccs    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.ascecm ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.bre (
+    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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.bmp    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
+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 NULL::XML $F$ LANGUAGE SQL STABLE;
+CREATE OR REPLACE FUNCTION unapi.circ   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
+
+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 NULL::XML $F$ LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION unapi.memoize (classname TEXT, 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$
+DECLARE
+    key     TEXT;
+    output  XML;
+BEGIN
+    key :=
+        'id'        || COALESCE(obj_id::TEXT,'') ||
+        'format'    || COALESCE(format::TEXT,'') ||
+        'ename'     || COALESCE(ename::TEXT,'') ||
+        'includes'  || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
+        'org'       || COALESCE(org::TEXT,'') ||
+        'depth'     || COALESCE(depth::TEXT,'') ||
+        'slimit'    || COALESCE(slimit::TEXT,'') ||
+        'soffset'   || COALESCE(soffset::TEXT,'') ||
+        'include_xmlns'   || COALESCE(include_xmlns::TEXT,'');
+    -- RAISE NOTICE 'memoize key: %', key;
 
--- NOT COVERED: Adding check_sms_notify to the proper trigger. It doesn't have a static id.
+    key := MD5(key);
+    -- RAISE NOTICE 'memoize hash: %', key;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0684', :eg_version);
+    -- XXX cache logic ... memcached? table?
 
--- schema --
+    EXECUTE $$SELECT unapi.$$ || classname || $$( $1, $2, $3, $4, $5, $6, $7, $8, $9);$$ INTO output USING obj_id, format, ename, includes, org, depth, slimit, soffset, include_xmlns;
+    RETURN output;
+END;
+$F$ LANGUAGE PLPGSQL STABLE;
 
--- Replace the constraints with more flexible ENUM's
-ALTER TABLE vandelay.queue DROP CONSTRAINT queue_queue_type_check;
-ALTER TABLE vandelay.bib_queue DROP CONSTRAINT bib_queue_queue_type_check;
-ALTER TABLE vandelay.authority_queue DROP CONSTRAINT authority_queue_queue_type_check;
+CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$
+DECLARE
+    layout          unapi.bre_output_layout%ROWTYPE;
+    transform       config.xml_transform%ROWTYPE;
+    item_format     TEXT;
+    tmp_xml         TEXT;
+    xmlns_uri       TEXT := 'http://open-ils.org/spec/feed-xml/v1';
+    ouid            INT;
+    element_list    TEXT[];
+BEGIN
 
-CREATE TYPE vandelay.bib_queue_queue_type AS ENUM ('bib', 'acq');
-CREATE TYPE vandelay.authority_queue_queue_type AS ENUM ('authority');
+    IF org = '-' OR org IS NULL THEN
+        SELECT shortname INTO org FROM evergreen.org_top();
+    END IF;
 
--- dropped column is also implemented by the child tables
-ALTER TABLE vandelay.queue DROP COLUMN queue_type; 
+    SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
+    SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
 
--- to recover after using the undo sql from below
--- alter table vandelay.bib_queue  add column queue_type text default 'bib' not null;
--- alter table vandelay.authority_queue  add column queue_type text default 'authority' not null;
+    IF layout.name IS NULL THEN
+        RETURN NULL::XML;
+    END IF;
 
--- modify the child tables to use the ENUMs
-ALTER TABLE vandelay.bib_queue 
-    ALTER COLUMN queue_type DROP DEFAULT,
-    ALTER COLUMN queue_type TYPE vandelay.bib_queue_queue_type 
-        USING (queue_type::vandelay.bib_queue_queue_type),
-    ALTER COLUMN queue_type SET DEFAULT 'bib';
+    SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
+    xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
 
-ALTER TABLE vandelay.authority_queue 
-    ALTER COLUMN queue_type DROP DEFAULT,
-    ALTER COLUMN queue_type TYPE vandelay.authority_queue_queue_type 
-        USING (queue_type::vandelay.authority_queue_queue_type),
-    ALTER COLUMN queue_type SET DEFAULT 'authority';
+    -- Gather the bib xml
+    SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
 
--- give lineitems a pointer to their vandelay queued_record
+    IF layout.title_element IS NOT NULL THEN
+        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
+    END IF;
 
-ALTER TABLE acq.lineitem ADD COLUMN queued_record BIGINT
-    REFERENCES vandelay.queued_bib_record (id) 
-    ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
+    IF layout.description_element IS NOT NULL THEN
+        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
+    END IF;
 
-ALTER TABLE acq.acq_lineitem_history ADD COLUMN queued_record BIGINT
-    REFERENCES vandelay.queued_bib_record (id) 
-    ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
+    IF layout.creator_element IS NOT NULL THEN
+        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
+    END IF;
 
--- seed data --
+    IF layout.update_ts_element IS NOT NULL THEN
+        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.update_ts_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, update_ts;
+    END IF;
 
-INSERT INTO permission.perm_list ( id, code, description ) 
-    VALUES ( 
-        521, 
-        'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD', 
-        oils_i18n_gettext( 
-            521,
-            'Allows a user to create new bibs directly from an ACQ MARC file upload', 
-            'ppl', 
-            'description' 
-        )
-    );
+    IF unapi_url IS NOT NULL THEN
+        EXECUTE $$SELECT XMLCONCAT( XMLELEMENT( name link, XMLATTRIBUTES( 'http://www.w3.org/1999/xhtml' AS xmlns, 'unapi-server' AS rel, $1 AS href, 'unapi' AS title)), $2)$$ INTO tmp_xml USING unapi_url, tmp_xml::XML;
+    END IF;
 
+    IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
 
-INSERT INTO vandelay.import_error ( code, description ) 
-    VALUES ( 
-        'import.record.perm_failure', 
-        oils_i18n_gettext(
-            'import.record.perm_failure', 
-            'Perm failure creating a record', 'vie', 'description') 
-    );
+    element_list := regexp_split_to_array(layout.feed_top,E'\\.');
+    FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
+        EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
+    END LOOP;
+
+    RETURN tmp_xml::XML;
+END;
+$F$ LANGUAGE PLPGSQL STABLE;
+
+CREATE OR REPLACE FUNCTION unapi.bre (
+    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
+    me      biblio.record_entry%ROWTYPE;
+    layout  unapi.bre_output_layout%ROWTYPE;
+    xfrm    config.xml_transform%ROWTYPE;
+    ouid    INT;
+    tmp_xml TEXT;
+    top_el  TEXT;
+    output  XML;
+    hxml    XML;
+    axml    XML;
+BEGIN
 
+    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;
 
--- Evergreen DB patch 0685.data.bluray_vr_format.sql
---
--- FIXME: insert description of change, if needed
---
+    IF format = 'holdings_xml' THEN -- the special case
+        output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
+        RETURN output;
+    END IF;
 
+    SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0685', :eg_version);
+    IF layout.name IS NULL THEN
+        RETURN NULL::XML;
+    END IF;
 
--- FIXME: add/check SQL statements to perform the upgrade
-DO $FUNC$
-DECLARE
-    same_marc BOOL;
-BEGIN
-    -- Check if it is already there
-    PERFORM * FROM config.marc21_physical_characteristic_value_map v
-        JOIN config.marc21_physical_characteristic_subfield_map s ON v.ptype_subfield = s.id
-        WHERE s.ptype_key = 'v' AND s.subfield = 'e' AND s.start_pos = '4' AND s.length = '1'
-            AND v.value = 's';
+    SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
 
-    -- If it is, bail.
-    IF FOUND THEN
-        RETURN;
+    SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
+
+    -- grab SVF if we need them
+    IF ('mra' = ANY (includes)) THEN 
+        axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
+    ELSE
+        axml := NULL::XML;
     END IF;
 
-    -- Otherwise, insert it
-    INSERT INTO config.marc21_physical_characteristic_value_map (value,ptype_subfield,label)
-    SELECT 's',id,'Blu-ray'
-        FROM config.marc21_physical_characteristic_subfield_map
-        WHERE ptype_key = 'v' AND subfield = 'e' AND start_pos = '4' AND length = '1';
+    -- grab holdings if we need them
+    IF ('holdings_xml' = ANY (includes)) THEN 
+        hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
+    ELSE
+        hxml := NULL::XML;
+    END IF;
 
-    -- And reingest the blue-ray items so that things see the new value
-    SELECT INTO same_marc enabled FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc';
-    UPDATE config.internal_flag SET enabled = true WHERE name = 'ingest.reingest.force_on_same_marc';
-    UPDATE biblio.record_entry SET marc=marc WHERE id IN (SELECT record
-        FROM
-            metabib.full_rec a JOIN metabib.full_rec b USING (record)
-        WHERE
-            a.tag = 'LDR' AND a.value LIKE '______g%'
-        AND b.tag = '007' AND b.value LIKE 'v___s%');
-    UPDATE config.internal_flag SET enabled = same_marc WHERE name = 'ingest.reingest.force_on_same_marc';
-END;
-$FUNC$;
 
+    -- generate our item node
 
--- Evergreen DB patch 0686.schema.auditor_boost.sql
---
--- FIXME: insert description of change, if needed
---
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0686', :eg_version);
 
--- FIXME: add/check SQL statements to perform the upgrade
--- These three functions are for capturing, getting, and clearing user and workstation information
+    IF format = 'marcxml' THEN
+        tmp_xml := me.marc;
+        IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
+           tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
+        END IF; 
+    ELSE
+        tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
+    END IF;
 
--- Set the User AND workstation in one call. Tis faster. And less calls.
--- First argument is user, second is workstation
-CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
-    $_SHARED{"eg_audit_user"} = $_[0];
-    $_SHARED{"eg_audit_ws"} = $_[1];
-$$ LANGUAGE plperl;
+    top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
 
--- Get the User AND workstation in one call. Less calls, useful for joins ;)
-CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
-    return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
-$$ LANGUAGE plperl;
+    IF axml IS NOT NULL THEN 
+        tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
+    END IF;
 
--- Clear the audit info, for whatever reason
-CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
-    delete($_SHARED{"eg_audit_user"});
-    delete($_SHARED{"eg_audit_ws"});
-$$ LANGUAGE plperl;
+    IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
+        tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
+    END IF;
 
-CREATE OR REPLACE FUNCTION auditor.create_auditor_history ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
-BEGIN
-    EXECUTE $$
-        CREATE TABLE auditor.$$ || sch || $$_$$ || tbl || $$_history (
-            audit_id	BIGINT				PRIMARY KEY,
-            audit_time	TIMESTAMP WITH TIME ZONE	NOT NULL,
-            audit_action	TEXT				NOT NULL,
-            audit_user  INT,
-            audit_ws    INT,
-            LIKE $$ || sch || $$.$$ || tbl || $$
+    IF ('bre.unapi' = ANY (includes)) THEN 
+        output := REGEXP_REPLACE(
+            tmp_xml,
+            '</' || top_el || '>(.*?)',
+            XMLELEMENT(
+                name abbr,
+                XMLATTRIBUTES(
+                    'http://www.w3.org/1999/xhtml' AS xmlns,
+                    'unapi-id' AS class,
+                    'tag:open-ils.org:U2 at bre/' || obj_id || '/' || org AS title
+                )
+            )::TEXT || '</' || top_el || E'>\\1'
         );
-    $$;
-	RETURN TRUE;
+    ELSE
+        output := tmp_xml;
+    END IF;
+
+    output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
+    RETURN output;
 END;
-$creator$ LANGUAGE 'plpgsql';
+$F$ LANGUAGE PLPGSQL STABLE;
 
-CREATE OR REPLACE FUNCTION auditor.create_auditor_func    ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
-DECLARE
-    column_list TEXT[];
-BEGIN
-    SELECT INTO column_list array_agg(a.attname)
-        FROM pg_catalog.pg_attribute a
-            JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
-            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-        WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
+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 at bre/' || $1 || '/' || $3 ELSE NULL END AS id
+                 ),
+                 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) 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), 0, 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;
 
-    EXECUTE $$
-        CREATE OR REPLACE FUNCTION auditor.audit_$$ || sch || $$_$$ || tbl || $$_func ()
-        RETURNS TRIGGER AS $func$
-        BEGIN
-            INSERT INTO auditor.$$ || sch || $$_$$ || tbl || $$_history ( audit_id, audit_time, audit_action, audit_user, audit_ws, $$
-            || array_to_string(column_list, ', ') || $$ )
-                SELECT  nextval('auditor.$$ || sch || $$_$$ || tbl || $$_pkey_seq'),
-                    now(),
-                    SUBSTR(TG_OP,1,1),
-                    eg_user,
-                    eg_ws,
-                    OLD.$$ || array_to_string(column_list, ', OLD.') || $$
-                FROM auditor.get_audit_info();
-            RETURN NULL;
-        END;
-        $func$ LANGUAGE 'plpgsql';
-    $$;
-    RETURN TRUE;
-END;
-$creator$ LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION unapi.ssub ( 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 subscription,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at ssub/' || id AS id,
+                        'tag:open-ils.org:U2 at aou/' || owning_lib AS owning_lib,
+                        start_date AS start, end_date AS end, expected_date_offset
+                    ),
+                    CASE 
+                        WHEN ('sdist' = ANY ($4)) THEN
+                            XMLELEMENT( name distributions,
+                                (SELECT XMLAGG(sdist) FROM (
+                                    SELECT  unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
+                                      FROM  serial.distribution
+                                      WHERE subscription = ssub.id
+                                )x)
+                            )
+                        ELSE NULL
+                    END
+                )
+          FROM  serial.subscription ssub
+          WHERE id = $1
+          GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
+$F$ LANGUAGE SQL STABLE;
 
-CREATE OR REPLACE FUNCTION auditor.create_auditor_lifecycle     ( sch TEXT, tbl TEXT ) RETURNS BOOL AS $creator$
-DECLARE
-    column_list TEXT[];
-BEGIN
-    SELECT INTO column_list array_agg(a.attname)
-        FROM pg_catalog.pg_attribute a
-            JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
-            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-        WHERE relkind = 'r' AND n.nspname = sch AND c.relname = tbl AND a.attnum > 0 AND NOT a.attisdropped;
+CREATE OR REPLACE FUNCTION unapi.sdist ( 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 distribution,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at sdist/' || id AS id,
+            			'tag:open-ils.org:U2 at acn/' || receive_call_number AS receive_call_number,
+			            'tag:open-ils.org:U2 at acn/' || bind_call_number AS bind_call_number,
+                        unit_label_prefix, label, unit_label_suffix, summary_method
+                    ),
+                    unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
+                    CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                    CASE 
+                        WHEN ('sstr' = ANY ($4)) THEN
+                            XMLELEMENT( name streams,
+                                (SELECT XMLAGG(sstr) FROM (
+                                    SELECT  unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
+                                      FROM  serial.stream
+                                      WHERE distribution = sdist.id
+                                )x)
+                            )
+                        ELSE NULL
+                    END,
+                    XMLELEMENT( name summaries,
+                        CASE 
+                            WHEN ('sbsum' = ANY ($4)) THEN
+                                (SELECT XMLAGG(sbsum) FROM (
+                                    SELECT  unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
+                                      FROM  serial.basic_summary
+                                      WHERE distribution = sdist.id
+                                )x)
+                            ELSE NULL
+                        END,
+                        CASE 
+                            WHEN ('sisum' = ANY ($4)) THEN
+                                (SELECT XMLAGG(sisum) FROM (
+                                    SELECT  unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
+                                      FROM  serial.index_summary
+                                      WHERE distribution = sdist.id
+                                )x)
+                            ELSE NULL
+                        END,
+                        CASE 
+                            WHEN ('sssum' = ANY ($4)) THEN
+                                (SELECT XMLAGG(sssum) FROM (
+                                    SELECT  unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
+                                      FROM  serial.supplement_summary
+                                      WHERE distribution = sdist.id
+                                )x)
+                            ELSE NULL
+                        END
+                    )
+                )
+          FROM  serial.distribution sdist
+          WHERE id = $1
+          GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
+$F$ LANGUAGE SQL STABLE;
 
-    EXECUTE $$
-        CREATE VIEW auditor.$$ || sch || $$_$$ || tbl || $$_lifecycle AS
-            SELECT -1 AS audit_id,
-                   now() AS audit_time,
-                   '-' AS audit_action,
-                   -1 AS audit_user,
-                   -1 AS audit_ws,
-                   $$ || array_to_string(column_list, ', ') || $$
-              FROM $$ || sch || $$.$$ || tbl || $$
-                UNION ALL
-            SELECT audit_id, audit_time, audit_action, audit_user, audit_ws,
-            $$ || array_to_string(column_list, ', ') || $$
-              FROM auditor.$$ || sch || $$_$$ || tbl || $$_history;
-    $$;
-    RETURN TRUE;
-END;
-$creator$ LANGUAGE 'plpgsql';
+CREATE OR REPLACE FUNCTION unapi.sstr ( 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 stream,
+                XMLATTRIBUTES(
+                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    'tag:open-ils.org:U2 at sstr/' || id AS id,
+                    routing_label
+                ),
+                CASE WHEN distribution IS NOT NULL AND ('sdist' = ANY ($4)) THEN unapi.sssum( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                CASE 
+                    WHEN ('sitem' = ANY ($4)) THEN
+                        XMLELEMENT( name items,
+                            (SELECT XMLAGG(sitem) FROM (
+                                SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
+                                  FROM  serial.item
+                                  WHERE stream = sstr.id
+                            )x)
+                        )
+                    ELSE NULL
+                END
+            )
+      FROM  serial.stream sstr
+      WHERE id = $1
+      GROUP BY id, routing_label, distribution;
+$F$ LANGUAGE SQL STABLE;
 
--- Corrects all column discrepencies between audit table and core table:
--- Adds missing columns
--- Removes leftover columns
--- Updates types
--- Also, ensures all core auditor columns exist.
-CREATE OR REPLACE FUNCTION auditor.fix_columns() RETURNS VOID AS $BODY$
-DECLARE
-    current_table TEXT = ''; -- Storage for post-loop main table name
-    current_audit_table TEXT = ''; -- Storage for post-loop audit table name
-    query TEXT = ''; -- Storage for built query
-    cr RECORD; -- column record object
-    alter_t BOOL = false; -- Has the alter table command been appended yet
-    auditor_cores TEXT[] = ARRAY[]::TEXT[]; -- Core auditor function list (filled inside of loop)
-    core_column TEXT; -- The current core column we are adding
-BEGIN
-    FOR cr IN
-        WITH audit_tables AS ( -- Basic grab of auditor tables. Anything in the auditor namespace, basically. With oids.
-            SELECT c.oid AS audit_oid, c.relname AS audit_table
-            FROM pg_catalog.pg_class c
-            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-            WHERE relkind='r' AND nspname = 'auditor'
-        ),
-        table_set AS ( -- Union of auditor tables with their "main" tables. With oids.
-            SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
-            FROM pg_catalog.pg_class c
-            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-            JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
-            WHERE relkind = 'r'
-        ),
-        column_lists AS ( -- All columns associated with the auditor or main table, grouped by the main table's oid.
-            SELECT DISTINCT ON (main_oid, attname) t.main_oid, a.attname
-            FROM table_set t
-            JOIN pg_catalog.pg_attribute a ON a.attrelid IN (t.main_oid, t.audit_oid)
-            WHERE attnum > 0 AND NOT attisdropped
-        ),
-        column_defs AS ( -- The motherload, every audit table and main table plus column names and defs.
-            SELECT audit_table,
-                   main_namespace,
-                   main_table,
-                   a.attname AS main_column, -- These two will be null for columns that have since been deleted, or for auditor core columns
-                   pg_catalog.format_type(a.atttypid, a.atttypmod) AS main_column_def,
-                   b.attname AS audit_column, -- These two will be null for columns that have since been added
-                   pg_catalog.format_type(b.atttypid, b.atttypmod) AS audit_column_def
-            FROM table_set t
-            JOIN column_lists c USING (main_oid)
-            LEFT JOIN pg_catalog.pg_attribute a ON a.attname = c.attname AND a.attrelid = t.main_oid AND a.attnum > 0 AND NOT a.attisdropped
-            LEFT JOIN pg_catalog.pg_attribute b ON b.attname = c.attname AND b.attrelid = t.audit_oid AND b.attnum > 0 AND NOT b.attisdropped
-        )
-        -- Nice sorted output from the above
-        SELECT * FROM column_defs WHERE main_column_def IS DISTINCT FROM audit_column_def ORDER BY main_namespace, main_table, main_column, audit_column
-    LOOP
-        IF current_table <> (cr.main_namespace || '.' || cr.main_table) THEN -- New table?
-            FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Update missing core auditor columns
-                IF NOT alter_t THEN -- Add ALTER TABLE if we haven't already
-                    query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
-                    alter_t:=TRUE;
-                ELSE
-                    query:=query || $$,$$;
-                END IF;
-                -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
-                query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
-            END LOOP;
-            IF alter_t THEN -- Open alter table = needs a semicolon
-                query:=query || $$; $$;
-                alter_t:=FALSE;
-                IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
-                    -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
-                    -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
-                    EXECUTE query;
-                    EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
-                        $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
-                    EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
-                        $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
-                    query:='';
-                END IF;
-            END IF;
-            -- New table means we reset the list of needed auditor core columns
-            auditor_cores = ARRAY['audit_id bigint', 'audit_time timestamp with time zone', 'audit_action text', 'audit_user integer', 'audit_ws integer'];
-            -- And store some values for use later, because we can't rely on cr in all places.
-            current_table:=cr.main_namespace || '.' || cr.main_table;
-            current_audit_table:=cr.audit_table;
-        END IF;
-        IF cr.main_column IS NULL AND cr.audit_column LIKE 'audit_%' THEN -- Core auditor column?
-            -- Remove core from list of cores
-            SELECT INTO auditor_cores array_agg(core) FROM unnest(auditor_cores) AS core WHERE core != (cr.audit_column || ' ' || cr.audit_column_def);
-        ELSIF cr.main_column IS NULL THEN -- Main column doesn't exist, and it isn't an auditor column. Needs dropping from the auditor.
-            IF NOT alter_t THEN
-                query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
-                alter_t:=TRUE;
-            ELSE
-                query:=query || $$,$$;
-            END IF;
-            query:=query || $$ DROP COLUMN $$ || cr.audit_column;
-        ELSIF cr.audit_column IS NULL AND cr.main_column IS NOT NULL THEN -- New column auditor doesn't have. Add it.
-            IF NOT alter_t THEN
-                query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
-                alter_t:=TRUE;
-            ELSE
-                query:=query || $$,$$;
-            END IF;
-            query:=query || $$ ADD COLUMN $$ || cr.main_column || $$ $$ || cr.main_column_def;
-        ELSIF cr.main_column IS NOT NULL AND cr.audit_column IS NOT NULL THEN -- Both sides have this column, but types differ. Fix that.
-            IF NOT alter_t THEN
-                query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
-                alter_t:=TRUE;
-            ELSE
-                query:=query || $$,$$;
-            END IF;
-            query:=query || $$ ALTER COLUMN $$ || cr.audit_column || $$ TYPE $$ || cr.main_column_def;
-        END IF;
-    END LOOP;
-    FOR core_column IN SELECT DISTINCT unnest(auditor_cores) LOOP -- Repeat this outside of the loop to catch the last table
-        IF NOT alter_t THEN
-            query:=query || $$ALTER TABLE auditor.$$ || current_audit_table;
-            alter_t:=TRUE;
-        ELSE
-            query:=query || $$,$$;
-        END IF;
-        -- Bit of a sneaky bit here. Create audit_id as a bigserial so it gets automatic values and doesn't complain about nulls when becoming a PRIMARY KEY.
-        query:=query || $$ ADD COLUMN $$ || CASE WHEN core_column = 'audit_id bigint' THEN $$audit_id bigserial PRIMARY KEY$$ ELSE core_column END;
-    END LOOP;
-    IF alter_t THEN -- Open alter table = needs a semicolon
-        query:=query || $$;$$;
-        IF 'audit_id bigint' = ANY(auditor_cores) THEN -- We added a primary key...
-            -- Fun! Drop the default on audit_id, drop the auto-created sequence, create a new one, and set the current value
-            -- For added fun, we have to execute in chunks due to the parser checking setval/currval arguments at parse time.
-            EXECUTE query;
-            EXECUTE $$ALTER TABLE auditor.$$ || current_audit_table || $$ ALTER COLUMN audit_id DROP DEFAULT; $$ ||
-                $$CREATE SEQUENCE auditor.$$ || current_audit_table || $$_pkey_seq;$$;
-            EXECUTE $$SELECT setval('auditor.$$ || current_audit_table || $$_pkey_seq', currval('auditor.$$ || current_audit_table || $$_audit_id_seq')); $$ ||
-                $$DROP SEQUENCE auditor.$$ || current_audit_table || $$_audit_id_seq;$$;
-            query:='';
-        END IF;
-    END IF;
-    EXECUTE query;
-END;
-$BODY$ LANGUAGE plpgsql;
+CREATE OR REPLACE FUNCTION unapi.siss ( 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 issuance,
+                XMLATTRIBUTES(
+                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    'tag:open-ils.org:U2 at siss/' || id AS id,
+                    create_date, edit_date, label, date_published,
+                    holding_code, holding_type, holding_link_id
+                ),
+                CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                CASE 
+                    WHEN ('sitem' = ANY ($4)) THEN
+                        XMLELEMENT( name items,
+                            (SELECT XMLAGG(sitem) FROM (
+                                SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
+                                  FROM  serial.item
+                                  WHERE issuance = sstr.id
+                            )x)
+                        )
+                    ELSE NULL
+                END
+            )
+      FROM  serial.issuance sstr
+      WHERE id = $1
+      GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
+$F$ LANGUAGE SQL STABLE;
 
--- Update it all routine
-CREATE OR REPLACE FUNCTION auditor.update_auditors() RETURNS boolean AS $BODY$
-DECLARE
-    auditor_name TEXT;
-    table_schema TEXT;
-    table_name TEXT;
-BEGIN
-    -- Drop Lifecycle view(s) before potential column changes
-    FOR auditor_name IN
-        SELECT c.relname
-            FROM pg_catalog.pg_class c
-                JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-            WHERE relkind = 'v' AND n.nspname = 'auditor' LOOP
-        EXECUTE $$ DROP VIEW auditor.$$ || auditor_name || $$;$$;
-    END LOOP;
-    -- Fix all column discrepencies
-    PERFORM auditor.fix_columns();
-    -- Re-create trigger functions and lifecycle views
-    FOR table_schema, table_name IN
-        WITH audit_tables AS (
-            SELECT c.oid AS audit_oid, c.relname AS audit_table
-            FROM pg_catalog.pg_class c
-            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-            WHERE relkind='r' AND nspname = 'auditor'
-        ),
-        table_set AS (
-            SELECT a.audit_oid, a.audit_table, c.oid AS main_oid, n.nspname as main_namespace, c.relname as main_table
-            FROM pg_catalog.pg_class c
-            JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-            JOIN audit_tables a ON a.audit_table = n.nspname || '_' || c.relname || '_history'
-            WHERE relkind = 'r'
-        )
-        SELECT main_namespace, main_table FROM table_set LOOP
-        
-        PERFORM auditor.create_auditor_func(table_schema, table_name);
-        PERFORM auditor.create_auditor_lifecycle(table_schema, table_name);
-    END LOOP;
-    RETURN TRUE;
-END;
-$BODY$ LANGUAGE plpgsql;
+CREATE OR REPLACE FUNCTION unapi.sitem ( 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 serial_item,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at sitem/' || id AS id,
+                        'tag:open-ils.org:U2 at siss/' || issuance AS issuance,
+                        date_expected, date_received
+                    ),
+                    CASE WHEN issuance IS NOT NULL AND ('siss' = ANY ($4)) THEN unapi.siss( issuance, $2, 'issuance', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                    CASE WHEN stream IS NOT NULL AND ('sstr' = ANY ($4)) THEN unapi.sstr( stream, $2, 'stream', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                    CASE WHEN unit IS NOT NULL AND ('sunit' = ANY ($4)) THEN unapi.sunit( unit, $2, 'serial_unit', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                    CASE WHEN uri IS NOT NULL AND ('auri' = ANY ($4)) THEN unapi.auri( uri, $2, 'uri', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END
+--                    XMLELEMENT( name notes,
+--                        CASE 
+--                            WHEN ('acpn' = ANY ($4)) THEN
+--                                (SELECT XMLAGG(acpn) FROM (
+--                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
+--                                      FROM  asset.copy_note
+--                                      WHERE owning_copy = cp.id AND pub
+--                                )x)
+--                            ELSE NULL
+--                        END
+--                    )
+                )
+          FROM  serial.item sitem
+          WHERE id = $1;
+$F$ LANGUAGE SQL STABLE;
 
--- Go ahead and update them all now
-SELECT auditor.update_auditors();
 
+CREATE OR REPLACE FUNCTION unapi.sssum ( 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 serial_summary,
+                XMLATTRIBUTES(
+                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    'tag:open-ils.org:U2 at sbsum/' || id AS id,
+                    'sssum' AS type, generated_coverage, textual_holdings, show_generated
+                ),
+                CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
+            )
+      FROM  serial.supplement_summary ssum
+      WHERE id = $1
+      GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
+$F$ LANGUAGE SQL STABLE;
 
--- Evergreen DB patch 0687.schema.enhance_reingest.sql
---
--- FIXME: insert description of change, if needed
---
+CREATE OR REPLACE FUNCTION unapi.sbsum ( 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 serial_summary,
+                XMLATTRIBUTES(
+                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    'tag:open-ils.org:U2 at sbsum/' || id AS id,
+                    'sbsum' AS type, generated_coverage, textual_holdings, show_generated
+                ),
+                CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
+            )
+      FROM  serial.basic_summary ssum
+      WHERE id = $1
+      GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
+$F$ LANGUAGE SQL STABLE;
 
+CREATE OR REPLACE FUNCTION unapi.sisum ( 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 serial_summary,
+                XMLATTRIBUTES(
+                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    'tag:open-ils.org:U2 at sbsum/' || id AS id,
+                    'sisum' AS type, generated_coverage, textual_holdings, show_generated
+                ),
+                CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
+            )
+      FROM  serial.index_summary ssum
+      WHERE id = $1
+      GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
+$F$ LANGUAGE SQL STABLE;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0687', :eg_version);
 
--- FIXME: add/check SQL statements to perform the upgrade
--- New function def
-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$
+CREATE OR REPLACE FUNCTION unapi.aou ( 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$
 DECLARE
-    fclass          RECORD;
-    ind_data        metabib.field_entry_template%ROWTYPE;
-    mbe_row         metabib.browse_entry%ROWTYPE;
-    mbe_id          BIGINT;
+    output XML;
 BEGIN
-    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
-    IF NOT FOUND THEN
-        IF NOT 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 skip_facet THEN
-            DELETE FROM metabib.facet_entry WHERE source = bib_id;
-        END IF;
-        IF NOT skip_browse THEN
-            DELETE FROM metabib.browse_entry_def_map WHERE source = bib_id;
-        END IF;
+    IF ename = 'circlib' THEN
+        SELECT  XMLELEMENT(
+                    name circlib,
+                    XMLATTRIBUTES(
+                        'http://open-ils.org/spec/actors/v1' AS xmlns,
+                        id AS ident
+                    ),
+                    name
+                ) INTO output
+          FROM  actor.org_unit aou
+          WHERE id = obj_id;
+    ELSE
+        EXECUTE $$SELECT  XMLELEMENT(
+                    name $$ || ename || $$,
+                    XMLATTRIBUTES(
+                        'http://open-ils.org/spec/actors/v1' AS xmlns,
+                        'tag:open-ils.org:U2 at aou/' || id AS id,
+                        shortname, name, opac_visible
+                    )
+                )
+          FROM  actor.org_unit aou
+         WHERE id = $1 $$ INTO output USING obj_id;
     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;
+    RETURN output;
 
-        IF ind_data.facet_field AND NOT skip_facet THEN
-            INSERT INTO metabib.facet_entry (field, source, value)
-                VALUES (ind_data.field, ind_data.source, ind_data.value);
-        END IF;
+END;
+$F$ LANGUAGE PLPGSQL STABLE;
 
-        IF ind_data.browse_field AND NOT 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;
+CREATE OR REPLACE FUNCTION unapi.acl ( 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 location,
+                XMLATTRIBUTES(
+                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    id AS ident,
+                    holdable,
+                    opac_visible,
+                    label_prefix AS prefix,
+                    label_suffix AS suffix
+                ),
+                name
+            )
+      FROM  asset.copy_location
+      WHERE id = $1;
+$F$ LANGUAGE SQL STABLE;
 
-            INSERT INTO metabib.browse_entry_def_map (entry, def, source)
-                VALUES (mbe_id, ind_data.field, ind_data.source);
-        END IF;
+CREATE OR REPLACE FUNCTION unapi.ccs ( 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 status,
+                XMLATTRIBUTES(
+                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    id AS ident,
+                    holdable,
+                    opac_visible
+                ),
+                name
+            )
+      FROM  config.copy_status
+      WHERE id = $1;
+$F$ LANGUAGE SQL STABLE;
 
-        IF ind_data.search_field AND NOT 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;
+CREATE OR REPLACE FUNCTION unapi.acpn ( 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 copy_note,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        create_date AS date,
+                        title
+                    ),
+                    value
+                )
+          FROM  asset.copy_note
+          WHERE id = $1;
+$F$ LANGUAGE SQL STABLE;
 
-    END LOOP;
+CREATE OR REPLACE FUNCTION unapi.ascecm ( 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 statcat,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        sc.name,
+                        sc.opac_visible
+                    ),
+                    asce.value
+                )
+          FROM  asset.stat_cat_entry asce
+                JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
+          WHERE asce.id = $1;
+$F$ LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION unapi.bmp ( 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 monograph_part,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at bmp/' || id AS id,
+                        id AS ident,
+                        label,
+                        label_sortkey,
+                        'tag:open-ils.org:U2 at bre/' || record AS record
+                    ),
+                    CASE 
+                        WHEN ('acp' = ANY ($4)) THEN
+                            XMLELEMENT( name copies,
+                                (SELECT XMLAGG(acp) FROM (
+                                    SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
+                                      FROM  asset.copy cp
+                                            JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
+                                      WHERE cpm.part = $1
+                                          AND cp.deleted IS FALSE
+                                      ORDER BY COALESCE(cp.copy_number,0), cp.barcode
+                                      LIMIT ($7 -> 'acp')::INT
+                                      OFFSET ($8 -> 'acp')::INT
+
+                                )x)
+                            )
+                        ELSE NULL
+                    END,
+                    CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE) ELSE NULL END
+                )
+          FROM  biblio.monograph_part
+          WHERE id = $1
+          GROUP BY id, label, label_sortkey, record;
+$F$ LANGUAGE SQL STABLE;
+
+CREATE OR REPLACE FUNCTION unapi.acp ( 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 copy,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at acp/' || id AS id, id AS copy_id,
+                        create_date, edit_date, copy_number, circulate, deposit,
+                        ref, holdable, deleted, deposit_amount, price, barcode,
+                        circ_modifier, circ_as_type, opac_visible, age_protect
+                    ),
+                    unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
+                    unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
+                    unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
+                    unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
+                    CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                    CASE 
+                        WHEN ('acpn' = ANY ($4)) THEN
+                            XMLELEMENT( name copy_notes,
+                                (SELECT XMLAGG(acpn) FROM (
+                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
+                                      FROM  asset.copy_note
+                                      WHERE owning_copy = cp.id AND pub
+                                )x)
+                            )
+                        ELSE NULL
+                    END,
+                    CASE 
+                        WHEN ('ascecm' = ANY ($4)) THEN
+                            XMLELEMENT( name statcats,
+                                (SELECT XMLAGG(ascecm) FROM (
+                                    SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
+                                      FROM  asset.stat_cat_entry_copy_map
+                                      WHERE owning_copy = cp.id
+                                )x)
+                            )
+                        ELSE NULL
+                    END,
+                    CASE
+                        WHEN ('bre' = ANY ($4)) THEN
+                            XMLELEMENT( name foreign_records,
+                                (SELECT XMLAGG(bre) FROM (
+                                    SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
+                                      FROM  biblio.peer_bib_copy_map
+                                      WHERE target_copy = cp.id
+                                )x)
 
-    RETURN;
-END;
-$func$ LANGUAGE PLPGSQL;
+                            )
+                        ELSE NULL
+                    END,
+                    CASE 
+                        WHEN ('bmp' = ANY ($4)) THEN
+                            XMLELEMENT( name monograph_parts,
+                                (SELECT XMLAGG(bmp) FROM (
+                                    SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
+                                      FROM  asset.copy_part_map
+                                      WHERE target_copy = cp.id
+                                )x)
+                            )
+                        ELSE NULL
+                    END,
+                    CASE 
+                        WHEN ('circ' = ANY ($4)) THEN
+                            XMLELEMENT( name current_circulation,
+                                (SELECT XMLAGG(circ) FROM (
+                                    SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
+                                      FROM  action.circulation
+                                      WHERE target_copy = cp.id
+                                            AND checkin_time IS NULL
+                                )x)
+                            )
+                        ELSE NULL
+                    END
+                )
+          FROM  asset.copy cp
+          WHERE id = $1
+              AND cp.deleted IS FALSE
+          GROUP BY id, status, location, circ_lib, call_number, create_date,
+              edit_date, copy_number, circulate, deposit, ref, holdable,
+              deleted, deposit_amount, price, barcode, circ_modifier,
+              circ_as_type, opac_visible, age_protect;
+$F$ LANGUAGE SQL STABLE;
 
--- Delete old one
-DROP FUNCTION IF EXISTS metabib.reingest_metabib_field_entries(BIGINT);
+CREATE OR REPLACE FUNCTION unapi.sunit ( 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 serial_unit,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at acp/' || id AS id, id AS copy_id,
+                        create_date, edit_date, copy_number, circulate, deposit,
+                        ref, holdable, deleted, deposit_amount, price, barcode,
+                        circ_modifier, circ_as_type, opac_visible, age_protect,
+                        status_changed_time, floating, mint_condition,
+                        detailed_contents, sort_key, summary_contents, cost 
+                    ),
+                    unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
+                    unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
+                    unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
+                    unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
+                    CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+                    XMLELEMENT( name copy_notes,
+                        CASE 
+                            WHEN ('acpn' = ANY ($4)) THEN
+                                (SELECT XMLAGG(acpn) FROM (
+                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE)
+                                      FROM  asset.copy_note
+                                      WHERE owning_copy = cp.id AND pub
+                                )x)
+                            ELSE NULL
+                        END
+                    ),
+                    XMLELEMENT( name statcats,
+                        CASE 
+                            WHEN ('ascecm' = ANY ($4)) THEN
+                                (SELECT XMLAGG(ascecm) FROM (
+                                    SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
+                                      FROM  asset.stat_cat_entry_copy_map
+                                      WHERE owning_copy = cp.id
+                                )x)
+                            ELSE NULL
+                        END
+                    ),
+                    XMLELEMENT( name foreign_records,
+                        CASE
+                            WHEN ('bre' = ANY ($4)) THEN
+                                (SELECT XMLAGG(bre) FROM (
+                                    SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
+                                      FROM  biblio.peer_bib_copy_map
+                                      WHERE target_copy = cp.id
+                                )x)
+                            ELSE NULL
+                        END
+                    ),
+                    CASE 
+                        WHEN ('bmp' = ANY ($4)) THEN
+                            XMLELEMENT( name monograph_parts,
+                                (SELECT XMLAGG(bmp) FROM (
+                                    SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
+                                      FROM  asset.copy_part_map
+                                      WHERE target_copy = cp.id
+                                )x)
+                            )
+                        ELSE NULL
+                    END,
+                    CASE 
+                        WHEN ('circ' = ANY ($4)) THEN
+                            XMLELEMENT( name current_circulation,
+                                (SELECT XMLAGG(circ) FROM (
+                                    SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
+                                      FROM  action.circulation
+                                      WHERE target_copy = cp.id
+                                            AND checkin_time IS NULL
+                                )x)
+                            )
+                        ELSE NULL
+                    END
+                )
+          FROM  serial.unit cp
+          WHERE id = $1
+              AND cp.deleted IS FALSE
+          GROUP BY id, status, location, circ_lib, call_number, create_date,
+              edit_date, copy_number, circulate, floating, mint_condition,
+              deposit, ref, holdable, deleted, deposit_amount, price,
+              barcode, circ_modifier, circ_as_type, opac_visible,
+              status_changed_time, detailed_contents, sort_key,
+              summary_contents, cost, age_protect;
+$F$ LANGUAGE SQL STABLE;
 
--- Evergreen DB patch 0688.data.circ_history_export_csv.sql
---
--- FIXME: insert description of change, if needed
---
+CREATE OR REPLACE FUNCTION unapi.acn ( 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 volume,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at acn/' || acn.id AS id,
+                        acn.id AS vol_id, o.shortname AS lib,
+                        o.opac_visible AS opac_visible,
+                        deleted, label, label_sortkey, label_class, record
+                    ),
+                    unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
+                    CASE 
+                        WHEN ('acp' = ANY ($4)) THEN
+                            CASE WHEN $6 IS NOT NULL THEN
+                                XMLELEMENT( name copies,
+                                    (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
+                                        SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
+                                            evergreen.rank_cp_status(cp.status) AS rank_avail
+                                          FROM  asset.copy cp
+                                                JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
+                                          WHERE cp.call_number = acn.id
+                                              AND cp.deleted IS FALSE
+                                          ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
+                                          LIMIT ($7 -> 'acp')::INT
+                                          OFFSET ($8 -> 'acp')::INT
+                                    )x)
+                                )
+                            ELSE
+                                XMLELEMENT( name copies,
+                                    (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
+                                        SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
+                                            evergreen.rank_cp_status(cp.status) AS rank_avail
+                                          FROM  asset.copy cp
+                                                JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
+                                          WHERE cp.call_number = acn.id
+                                              AND cp.deleted IS FALSE
+                                          ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
+                                          LIMIT ($7 -> 'acp')::INT
+                                          OFFSET ($8 -> 'acp')::INT
+                                    )x)
+                                )
+                            END
+                        ELSE NULL
+                    END,
+                    XMLELEMENT(
+                        name uris,
+                        (SELECT XMLAGG(auri) FROM (SELECT unapi.auri(uri,'xml','uri', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE call_number = acn.id)x)
+                    ),
+                    unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
+                    unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
+                    CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( acn.record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) ELSE NULL END
+                ) AS x
+          FROM  asset.call_number acn
+                JOIN actor.org_unit o ON (o.id = acn.owning_lib)
+          WHERE acn.id = $1
+              AND acn.deleted IS FALSE
+          GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
+$F$ LANGUAGE SQL STABLE;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0688', :eg_version);
+CREATE OR REPLACE FUNCTION unapi.acnp ( 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 call_number_prefix,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        id AS ident,
+                        label,
+                        'tag:open-ils.org:U2 at aou/' || owning_lib AS owning_lib,
+                        label_sortkey
+                    )
+                )
+          FROM  asset.call_number_prefix
+          WHERE id = $1;
+$F$ LANGUAGE SQL STABLE;
 
-INSERT INTO action_trigger.hook (key, core_type, description, passive)
-VALUES (
-    'circ.format.history.csv',
-    'circ',
-    oils_i18n_gettext(
-        'circ.format.history.csv',
-        'Produce CSV of circulation history',
-        'ath',
-        'description'
-    ),
-    FALSE
-);
+CREATE OR REPLACE FUNCTION unapi.acns ( 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 call_number_suffix,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        id AS ident,
+                        label,
+                        'tag:open-ils.org:U2 at aou/' || owning_lib AS owning_lib,
+                        label_sortkey
+                    )
+                )
+          FROM  asset.call_number_suffix
+          WHERE id = $1;
+$F$ LANGUAGE SQL STABLE;
 
-INSERT INTO action_trigger.event_definition (
-    active, owner, name, hook, reactor, validator, group_field, template) 
-VALUES (
-    TRUE, 1, 'Circ History CSV', 'circ.format.history.csv', 'ProcessTemplate', 'NOOP_True', 'usr',
-$$
-Title,Author,Call Number,Barcode,Format
-[%-
-FOR circ IN target;
-    bibxml = helpers.unapi_bre(circ.target_copy.call_number.record, {flesh => '{mra}'});
-    title = "";
-    FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
-        title = title _ part.textContent;
-    END;
-    author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
-    item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value') %]
+CREATE OR REPLACE FUNCTION unapi.auri ( 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 uri,
+                    XMLATTRIBUTES(
+                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                        'tag:open-ils.org:U2 at auri/' || uri.id AS id,
+                        use_restriction,
+                        href,
+                        label
+                    ),
+                    CASE 
+                        WHEN ('acn' = ANY ($4)) THEN
+                            XMLELEMENT( name copies,
+                                (SELECT XMLAGG(acn) FROM (SELECT unapi.acn( call_number, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'auri'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE uri = uri.id)x)
+                            )
+                        ELSE NULL
+                    END
+                ) AS x
+          FROM  asset.uri uri
+          WHERE uri.id = $1
+          GROUP BY uri.id, use_restriction, href, label;
+$F$ LANGUAGE SQL STABLE;
 
-    [%- helpers.csv_datum(title) -%],
-    [%- helpers.csv_datum(author) -%],
-    [%- helpers.csv_datum(circ.target_copy.call_number.label) -%],
-    [%- helpers.csv_datum(circ.target_copy.barcode) -%],
-    [%- helpers.csv_datum(item_type) %]
-[%- END -%]
-$$
-);
+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 at mra/' || mra.id AS id,
+                        'tag:open-ils.org:U2 at bre/' || mra.id AS record
+                    ),
+                    (SELECT XMLAGG(foo.y)
+                      FROM (SELECT XMLELEMENT(
+                                name field,
+                                XMLATTRIBUTES(
+                                    key AS name,
+                                    cvm.value AS "coded-value",
+                                    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;
 
-INSERT INTO action_trigger.environment (event_def, path)
-    VALUES (
-        currval('action_trigger.event_definition_id_seq'),
-        'target_copy.call_number'
-    );
+CREATE OR REPLACE FUNCTION unapi.circ (obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
+    SELECT XMLELEMENT(
+        name circ,
+        XMLATTRIBUTES(
+            CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+            'tag:open-ils.org:U2 at circ/' || id AS id,
+            xact_start,
+            due_date
+        ),
+        CASE WHEN ('aou' = ANY ($4)) THEN unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END,
+        CASE WHEN ('acp' = ANY ($4)) THEN unapi.acp( circ_lib, $2, 'target_copy', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END
+    )
+    FROM action.circulation
+    WHERE id = $1;
+$F$ LANGUAGE SQL STABLE;
 
+/*
 
--- Evergreen DB patch 0689.data.record_print_format_update.sql
---
--- Updates print and email templates for bib record actions
---
+ -- Some test queries
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0689', :eg_version);
+SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
+SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
+SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
 
-UPDATE action_trigger.event_definition SET template = $$
-<div>
-    <style> li { padding: 8px; margin 5px; }</style>
-    <ol>
-    [% FOR cbreb IN target %]
-    [% FOR item IN cbreb.items;
-        bre_id = item.target_biblio_record_entry;
+SELECT unapi.biblio_record_entry_feed('{1}'::BIGINT[],'mods32','{holdings_xml,acp}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://c64/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
 
-        bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
-        FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
-            title = title _ part.textContent;
-        END;
+SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
+EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
+EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
+EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'mods32','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
 
-        author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
-        item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
-        publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
-        pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
-        isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
-        issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
-        upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
-        %]
+SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
+EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
+EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
+EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
+EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
 
-        <li>
-            Bib ID# [% bre_id %]<br/>
-            [% IF isbn %]ISBN: [% isbn %]<br/>[% END %]
-            [% IF issn %]ISSN: [% issn %]<br/>[% END %]
-            [% IF upc  %]UPC:  [% upc %]<br/>[% END %]
-            Title: [% title %]<br />
-            Author: [% author %]<br />
-            Publication Info: [% publisher %] [% pubdate %]<br/>
-            Item Type: [% item_type %]
-        </li>
-    [% END %]
-    [% END %]
-    </ol>
-</div>
-$$ 
-WHERE hook = 'biblio.format.record_entry.print' AND id < 100; -- sample data
+-- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
+EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
 
+*/
 
-UPDATE action_trigger.event_definition SET delay = '00:00:00', template = $$
-[%- SET user = target.0.owner -%]
-To: [%- params.recipient_email || user.email %]
-From: [%- params.sender_email || default_sender %]
-Subject: Bibliographic Records
 
-[% FOR cbreb IN target %]
-[% FOR item IN cbreb.items;
-    bre_id = item.target_biblio_record_entry;
+SELECT evergreen.upgrade_deps_block_check('0691', :eg_version);
 
-    bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
-    FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
-        title = title _ part.textContent;
-    END;
+CREATE INDEX poi_po_idx ON acq.po_item (purchase_order);
 
-    author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
-    item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
-    publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
-    pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
-    isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
-    issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
-    upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
-%]
+CREATE INDEX ie_inv_idx on acq.invoice_entry (invoice);
+CREATE INDEX ie_po_idx on acq.invoice_entry (purchase_order);
+CREATE INDEX ie_li_idx on acq.invoice_entry (lineitem);
 
-[% loop.count %]/[% loop.size %].  Bib ID# [% bre_id %] 
-[% IF isbn %]ISBN: [% isbn _ "\n" %][% END -%]
-[% IF issn %]ISSN: [% issn _ "\n" %][% END -%]
-[% IF upc  %]UPC:  [% upc _ "\n" %] [% END -%]
-Title: [% title %]
-Author: [% author %]
-Publication Info: [% publisher %] [% pubdate %]
-Item Type: [% item_type %]
+CREATE INDEX ii_inv_idx on acq.invoice_item (invoice);
+CREATE INDEX ii_po_idx on acq.invoice_item (purchase_order);
+CREATE INDEX ii_poi_idx on acq.invoice_item (po_item);
 
-[% END %]
-[% END %]
-$$ 
-WHERE hook = 'biblio.format.record_entry.email' AND id < 100; -- sample data
 
--- remove a swath of unused environment entries
+SELECT evergreen.upgrade_deps_block_check('0692', :eg_version);
 
-DELETE FROM action_trigger.environment env 
-    USING action_trigger.event_definition def 
-    WHERE env.event_def = def.id AND 
-        env.path != 'items' AND 
-        def.hook = 'biblio.format.record_entry.print' AND 
-        def.id < 100; -- sample data
+INSERT INTO config.org_unit_setting_type
+    (name, label, description, grp, datatype)
+    VALUES (
+        'circ.fines.charge_when_closed',
+         oils_i18n_gettext(
+            'circ.fines.charge_when_closed',
+            'Charge fines on overdue circulations when closed',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'circ.fines.charge_when_closed',
+            'Normally, fines are not charged when a library is closed.  When set to True, fines will be charged during scheduled closings and normal weekly closed days.',
+            'coust',
+            'description'
+        ),
+        'circ',
+        'bool'
+    );
 
-DELETE FROM action_trigger.environment env 
-    USING action_trigger.event_definition def 
-    WHERE env.event_def = def.id AND 
-        env.path != 'items' AND 
-        env.path != 'owner' AND 
-        def.hook = 'biblio.format.record_entry.email' AND 
-        def.id < 100; -- sample data
+SELECT evergreen.upgrade_deps_block_check('0694', :eg_version);
 
--- Evergreen DB patch 0690.schema.unapi_limit_rank.sql
---
--- Rewrite the in-database unapi functions to include per-object limits and
--- offsets, such as a maximum number of copies and call numbers for given
--- bib record via the HSTORE syntax (for example, 'acn => 5, acp => 10' would
--- limit to a maximum of 5 call numbers for the bib, with up to 10 copies per
--- call number).
---
--- Add some notion of "preferred library" that will provide copy counts
--- and optionally affect the sorting of returned copies.
---
--- Sort copies by availability, preferring the most available copies.
---
--- Return located URIs.
---
---
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0690', :eg_version);
+( 'ui.patron.edit.au.prefix.require', 'gui',
+    oils_i18n_gettext('ui.patron.edit.au.prefix.require',
+        'Require prefix field on patron registration',
+        'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.prefix.require',
+        'The prefix field will be required on the patron registration screen.',
+        'coust', 'description'),
+    'bool', null)
+	
+,( 'ui.patron.edit.au.prefix.show', 'gui',
+    oils_i18n_gettext('ui.patron.edit.au.prefix.show',
+        'Show prefix field on patron registration',
+        'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.prefix.show',
+        'The prefix 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', null)
 
--- The simplest way to apply all of these changes is just to replace the unapi
--- schema entirely -- the following is a copy of 990.schema.unapi.sql with
--- the initial COMMIT in place in case the upgrade_deps_block_check fails;
--- if it does, then the attempt to create the unapi schema in the following
--- transaction will also fail. Not graceful, but safe!
-DROP SCHEMA IF EXISTS unapi CASCADE;
+,( 'ui.patron.edit.au.prefix.suggest', 'gui',
+    oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
+        'Suggest prefix field on patron registration',
+        'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
+        'The prefix 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', null)
+;		
 
-CREATE SCHEMA unapi;
 
-CREATE OR REPLACE FUNCTION evergreen.org_top()
-RETURNS SETOF actor.org_unit AS $$
-    SELECT * FROM actor.org_unit WHERE parent_ou IS NULL LIMIT 1;
-$$ LANGUAGE SQL STABLE
-ROWS 1;
+-- Evergreen DB patch 0695.schema.custom_toolbars.sql
+--
+-- FIXME: insert description of change, if needed
+--
 
-CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT)
-RETURNS anyarray AS $$
-    SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2;
-$$ LANGUAGE SQL STABLE;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0695', :eg_version);
 
-CREATE OR REPLACE FUNCTION evergreen.rank_ou(lib INT, search_lib INT, pref_lib INT DEFAULT NULL)
-RETURNS INTEGER AS $$
-    WITH search_libs AS (
-        SELECT id, distance FROM actor.org_unit_descendants_distance($2)
-    )
-    SELECT COALESCE(
-        (SELECT -10000 FROM actor.org_unit
-         WHERE $1 = $3 AND id = $3 AND $2 IN (
-                SELECT id FROM actor.org_unit WHERE parent_ou IS NULL
-             )
-        ),
-        (SELECT distance FROM search_libs WHERE id = $1),
-        10000
-    );
-$$ LANGUAGE SQL STABLE;
+CREATE TABLE actor.toolbar (
+    id          BIGSERIAL   PRIMARY KEY,
+    ws          INT         REFERENCES actor.workstation (id) ON DELETE CASCADE,
+    org         INT         REFERENCES actor.org_unit (id) ON DELETE CASCADE,
+    usr         INT         REFERENCES actor.usr (id) ON DELETE CASCADE,
+    label       TEXT        NOT NULL,
+    layout      TEXT        NOT NULL,
+    CONSTRAINT only_one_type CHECK (
+        (ws IS NOT NULL AND COALESCE(org,usr) IS NULL) OR
+        (org IS NOT NULL AND COALESCE(ws,usr) IS NULL) OR
+        (usr IS NOT NULL AND COALESCE(org,ws) IS NULL)
+    ),
+    CONSTRAINT layout_must_be_json CHECK ( is_json(layout) )
+);
+CREATE UNIQUE INDEX label_once_per_ws ON actor.toolbar (ws, label) WHERE ws IS NOT NULL;
+CREATE UNIQUE INDEX label_once_per_org ON actor.toolbar (org, label) WHERE org IS NOT NULL;
+CREATE UNIQUE INDEX label_once_per_usr ON actor.toolbar (usr, label) WHERE usr IS NOT NULL;
 
-CREATE OR REPLACE FUNCTION evergreen.rank_cp_status(status INT)
-RETURNS INTEGER AS $$
-    WITH totally_available AS (
-        SELECT id, 0 AS avail_rank
-        FROM config.copy_status
-        WHERE opac_visible IS TRUE
-            AND copy_active IS TRUE
-            AND id != 1 -- "Checked out"
-    ), almost_available AS (
-        SELECT id, 10 AS avail_rank
-        FROM config.copy_status
-        WHERE holdable IS TRUE
-            AND opac_visible IS TRUE
-            AND copy_active IS FALSE
-            OR id = 1 -- "Checked out"
-    )
-    SELECT COALESCE(
-        (SELECT avail_rank FROM totally_available WHERE $1 IN (id)),
-        (SELECT avail_rank FROM almost_available WHERE $1 IN (id)),
-        100
+-- this one unrelated to toolbars but is a gap in the upgrade scripts
+INSERT INTO permission.perm_list ( id, code, description )
+    SELECT
+        522,
+        'IMPORT_AUTHORITY_MARC',
+        oils_i18n_gettext(
+            522,
+            'Allows a user to create new authority records',
+            'ppl',
+            'description'
+        )
+    WHERE NOT EXISTS (
+        SELECT 1
+        FROM permission.perm_list
+        WHERE
+            id = 522
     );
-$$ 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
-) 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 = $1
-            AND acn.deleted IS FALSE
-            AND acp.deleted IS FALSE
-        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;
+INSERT INTO permission.perm_list ( id, code, description ) VALUES (
+    523,
+    'ADMIN_TOOLBAR',
+    oils_i18n_gettext(
+        523,
+        'Allows a user to create, edit, and delete custom toolbars',
+        'ppl',
+        'description'
+    )
+);
 
-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 acn.id, aou.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
-           INNER JOIN actor.org_unit_ancestors( COALESCE($3, $2) ) aou ON (acn.owning_lib = aou.id)
-      WHERE acn.record = $1
-          AND acn.deleted IS FALSE
-          AND auri.active IS TRUE
-    UNION
-    SELECT acn.id, aou.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
-           INNER JOIN actor.org_unit_ancestors( $2 ) aou ON (acn.owning_lib = aou.id)
-      WHERE acn.record = $1
-          AND acn.deleted IS FALSE
-          AND auri.active IS TRUE;
-$$
-LANGUAGE SQL STABLE;
+-- Don't want to assume stock perm groups in an upgrade script, but here for ease of testing
+-- INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) SELECT pgt.id, perm.id, aout.depth, FALSE FROM permission.grp_tree pgt, permission.perm_list perm, actor.org_unit_type aout WHERE pgt.name = 'Staff' AND aout.name = 'Branch' AND perm.code = 'ADMIN_TOOLBAR';
 
-CREATE TABLE unapi.bre_output_layout (
-    name                TEXT    PRIMARY KEY,
-    transform           TEXT    REFERENCES config.xml_transform (name) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    mime_type           TEXT    NOT NULL,
-    feed_top            TEXT    NOT NULL,
-    holdings_element    TEXT,
-    title_element       TEXT,
-    description_element TEXT,
-    creator_element     TEXT,
-    update_ts_element   TEXT
-);
+INSERT INTO actor.toolbar(org,label,layout) VALUES
+    ( 1, 'circ', '["circ_checkout","circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","patron_search","patron_register","toolbarspacer.3","hotkeys_toggle"]' ),
+    ( 1, 'cat', '["circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","create_marc","authority_manage","retrieve_last_record","toolbarspacer.3","hotkeys_toggle"]' );
 
-INSERT INTO unapi.bre_output_layout
-    (name,           transform, mime_type,              holdings_element, feed_top,         title_element, description_element, creator_element, update_ts_element)
-        VALUES
-    ('holdings_xml', NULL,      'application/xml',      NULL,             'hxml',           NULL,          NULL,                NULL,            NULL),
-    ('marcxml',      'marcxml', 'application/marc+xml', 'record',         'collection',     NULL,          NULL,                NULL,            NULL),
-    ('mods32',       'mods32',  'application/mods+xml', 'mods',           'modsCollection', NULL,          NULL,                NULL,            NULL)
-;
+-- delete from permission.grp_perm_map where perm in (select id from permission.perm_list where code ~ 'TOOLBAR'); delete from permission.perm_list where code ~ 'TOOLBAR'; drop table actor.toolbar ;
 
--- Dummy functions, so we can create the real ones out of order
-CREATE OR REPLACE FUNCTION unapi.aou    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.acnp   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.acns   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.acn    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.ssub   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.sdist  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.sstr   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.sitem  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.sunit  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.sisum  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.sbsum  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.sssum  ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.siss   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.auri   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.acp    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.acpn   ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.acl    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.ccs    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.ascecm ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.bre (
-    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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.bmp    ( 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 NULL::XML $F$ LANGUAGE SQL STABLE;
-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 NULL::XML $F$ LANGUAGE SQL STABLE;
-CREATE OR REPLACE FUNCTION unapi.circ   ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
+-- Evergreen DB patch 0696.no_plperl.sql
+--
+-- FIXME: insert description of change, if needed
+--
 
-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 NULL::XML $F$ LANGUAGE SQL STABLE;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0696', :eg_version);
 
-CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL STABLE;
+-- Re-create these as plperlu instead of plperl
+CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
+    $_SHARED{"eg_audit_user"} = $_[0];
+    $_SHARED{"eg_audit_ws"} = $_[1];
+$$ LANGUAGE plperlu;
 
-CREATE OR REPLACE FUNCTION unapi.memoize (classname TEXT, 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$
-DECLARE
-    key     TEXT;
-    output  XML;
-BEGIN
-    key :=
-        'id'        || COALESCE(obj_id::TEXT,'') ||
-        'format'    || COALESCE(format::TEXT,'') ||
-        'ename'     || COALESCE(ename::TEXT,'') ||
-        'includes'  || COALESCE(includes::TEXT,'{}'::TEXT[]::TEXT) ||
-        'org'       || COALESCE(org::TEXT,'') ||
-        'depth'     || COALESCE(depth::TEXT,'') ||
-        'slimit'    || COALESCE(slimit::TEXT,'') ||
-        'soffset'   || COALESCE(soffset::TEXT,'') ||
-        'include_xmlns'   || COALESCE(include_xmlns::TEXT,'');
-    -- RAISE NOTICE 'memoize key: %', key;
+CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
+    return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
+$$ LANGUAGE plperlu;
 
-    key := MD5(key);
-    -- RAISE NOTICE 'memoize hash: %', key;
+CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
+    delete($_SHARED{"eg_audit_user"});
+    delete($_SHARED{"eg_audit_ws"});
+$$ LANGUAGE plperlu;
 
-    -- XXX cache logic ... memcached? table?
+-- And remove the language so that we don't use it later.
+DROP LANGUAGE plperl;
 
-    EXECUTE $$SELECT unapi.$$ || classname || $$( $1, $2, $3, $4, $5, $6, $7, $8, $9);$$ INTO output USING obj_id, format, ename, includes, org, depth, slimit, soffset, include_xmlns;
-    RETURN output;
-END;
-$F$ LANGUAGE PLPGSQL STABLE;
+-- Evergreen DB patch 0697.data.place_currently_unfillable_hold.sql
+--
+-- FIXME: insert description of change, if needed
+--
 
-CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$
-DECLARE
-    layout          unapi.bre_output_layout%ROWTYPE;
-    transform       config.xml_transform%ROWTYPE;
-    item_format     TEXT;
-    tmp_xml         TEXT;
-    xmlns_uri       TEXT := 'http://open-ils.org/spec/feed-xml/v1';
-    ouid            INT;
-    element_list    TEXT[];
-BEGIN
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0697', :eg_version);
 
-    IF org = '-' OR org IS NULL THEN
-        SELECT shortname INTO org FROM evergreen.org_top();
-    END IF;
+-- FIXME: add/check SQL statements to perform the upgrade
+INSERT INTO permission.perm_list ( id, code, description ) VALUES
+ ( 524, 'PLACE_UNFILLABLE_HOLD', oils_i18n_gettext( 524,
+    'Allows a user to place a hold that cannot currently be filled.', 'ppl', 'description' ));
 
-    SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
-    SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
+-- Evergreen DB patch 0698.hold_default_pickup.sql
+--
+-- FIXME: insert description of change, if needed
+--
 
-    IF layout.name IS NULL THEN
-        RETURN NULL::XML;
-    END IF;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0698', :eg_version);
 
-    SELECT * INTO transform FROM config.xml_transform WHERE name = layout.transform;
-    xmlns_uri := COALESCE(transform.namespace_uri,xmlns_uri);
+INSERT INTO config.usr_setting_type (name,opac_visible,label,description,datatype)
+    VALUES ('opac.default_pickup_location', TRUE, 'Default Hold Pickup Location', 'Default location for holds pickup', 'integer');
 
-    -- Gather the bib xml
-    SELECT XMLAGG( unapi.bre(i, format, '', includes, org, depth, slimit, soffset, include_xmlns)) INTO tmp_xml FROM UNNEST( id_list ) i;
+SELECT evergreen.upgrade_deps_block_check('0699', :eg_version);
 
-    IF layout.title_element IS NOT NULL THEN
-        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.title_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, title;
-    END IF;
+INSERT INTO config.org_unit_setting_type ( name, label, description, datatype, grp )
+    VALUES (
+        'ui.hide_copy_editor_fields',
+        oils_i18n_gettext(
+            'ui.hide_copy_editor_fields',
+            'GUI: Hide these fields within the Item Attribute Editor',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'ui.hide_copy_editor_fields',
+            'This setting may be best maintained with the dedicated configuration'
+            || ' interface within the Item Attribute Editor.  However, here it'
+            || ' shows up as comma separated list of field identifiers to hide.',
+            'coust',
+            'description'
+        ),
+        'array',
+        'gui'
+    );
 
-    IF layout.description_element IS NOT NULL THEN
-        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.description_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, description;
-    END IF;
 
-    IF layout.creator_element IS NOT NULL THEN
-        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.creator_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, creator;
-    END IF;
+SELECT evergreen.upgrade_deps_block_check('0700', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('0706', :eg_version);
 
-    IF layout.update_ts_element IS NOT NULL THEN
-        EXECUTE 'SELECT XMLCONCAT( XMLELEMENT( name '|| layout.update_ts_element ||', XMLATTRIBUTES( $1 AS xmlns), $3), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML, update_ts;
-    END IF;
+-- This throws away data, but only data that causes breakage anyway.
+UPDATE serial.issuance SET holding_code = NULL WHERE NOT is_json(holding_code);
 
-    IF unapi_url IS NOT NULL THEN
-        EXECUTE $$SELECT XMLCONCAT( XMLELEMENT( name link, XMLATTRIBUTES( 'http://www.w3.org/1999/xhtml' AS xmlns, 'unapi-server' AS rel, $1 AS href, 'unapi' AS title)), $2)$$ INTO tmp_xml USING unapi_url, tmp_xml::XML;
-    END IF;
+-- If we don't do this, we have unprocessed triggers and we can't alter the table
+SET CONSTRAINTS serial.issuance_caption_and_pattern_fkey IMMEDIATE;
 
-    IF header_xml IS NOT NULL THEN tmp_xml := XMLCONCAT(header_xml,tmp_xml::XML); END IF;
+ALTER TABLE serial.issuance ADD CHECK (holding_code IS NULL OR is_json(holding_code));
 
-    element_list := regexp_split_to_array(layout.feed_top,E'\\.');
-    FOR i IN REVERSE ARRAY_UPPER(element_list, 1) .. 1 LOOP
-        EXECUTE 'SELECT XMLELEMENT( name '|| quote_ident(element_list[i]) ||', XMLATTRIBUTES( $1 AS xmlns), $2)' INTO tmp_xml USING xmlns_uri, tmp_xml::XML;
-    END LOOP;
+INSERT INTO config.internal_flag (name, value, enabled) VALUES (
+    'serial.rematerialize_on_same_holding_code', NULL, FALSE
+);
 
-    RETURN tmp_xml::XML;
-END;
-$F$ LANGUAGE PLPGSQL STABLE;
+INSERT INTO config.org_unit_setting_type (
+    name, label, grp, description, datatype
+) VALUES (
+    'serial.default_display_grouping',
+    'Default display grouping for serials distributions presented in the OPAC.',
+    'serial',
+    'Default display grouping for serials distributions presented in the OPAC. This can be "enum" or "chron".',
+    'string'
+);
 
-CREATE OR REPLACE FUNCTION unapi.bre (
-    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
-    me      biblio.record_entry%ROWTYPE;
-    layout  unapi.bre_output_layout%ROWTYPE;
-    xfrm    config.xml_transform%ROWTYPE;
-    ouid    INT;
-    tmp_xml TEXT;
-    top_el  TEXT;
-    output  XML;
-    hxml    XML;
-    axml    XML;
-BEGIN
+ALTER TABLE serial.distribution
+    ADD COLUMN display_grouping TEXT NOT NULL DEFAULT 'chron'
+        CHECK (display_grouping IN ('enum', 'chron'));
 
-    IF org = '-' OR org IS NULL THEN
-        SELECT shortname INTO org FROM evergreen.org_top();
-    END IF;
+-- why didn't we just make one summary table in the first place?
+CREATE VIEW serial.any_summary AS
+    SELECT
+        'basic' AS summary_type, id, distribution,
+        generated_coverage, textual_holdings, show_generated
+    FROM serial.basic_summary
+    UNION
+    SELECT
+        'index' AS summary_type, id, distribution,
+        generated_coverage, textual_holdings, show_generated
+    FROM serial.index_summary
+    UNION
+    SELECT
+        'supplement' AS summary_type, id, distribution,
+        generated_coverage, textual_holdings, show_generated
+    FROM serial.supplement_summary ;
 
-    SELECT id INTO ouid FROM actor.org_unit WHERE shortname = org;
 
-    IF ouid IS NULL THEN
-        RETURN NULL::XML;
-    END IF;
+-- Given the IDs of two rows in actor.org_unit, *the second being an ancestor
+-- of the first*, return in array form the path from the ancestor to the
+-- descendant, with each point in the path being an org_unit ID.  This is
+-- useful for sorting org_units by their position in a depth-first (display
+-- order) representation of the tree.
+--
+-- This breaks with the precedent set by actor.org_unit_full_path() and others,
+-- and gets the parameters "backwards," but otherwise this function would
+-- not be very usable within json_query.
+CREATE OR REPLACE FUNCTION actor.org_unit_simple_path(INT, INT)
+RETURNS INT[] AS $$
+    WITH RECURSIVE descendant_depth(id, path) AS (
+        SELECT  aou.id,
+                ARRAY[aou.id]
+          FROM  actor.org_unit aou
+                JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
+          WHERE aou.id = $2
+            UNION ALL
+        SELECT  aou.id,
+                dd.path || ARRAY[aou.id]
+          FROM  actor.org_unit aou
+                JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
+                JOIN descendant_depth dd ON (dd.id = aou.parent_ou)
+    ) SELECT dd.path
+        FROM actor.org_unit aou
+        JOIN descendant_depth dd USING (id)
+        WHERE aou.id = $1 ORDER BY dd.path;
+$$ LANGUAGE SQL STABLE;
 
-    IF format = 'holdings_xml' THEN -- the special case
-        output := unapi.holdings_xml( obj_id, ouid, org, depth, includes, slimit, soffset, include_xmlns);
-        RETURN output;
-    END IF;
+CREATE TABLE serial.materialized_holding_code (
+    id BIGSERIAL PRIMARY KEY,
+    issuance INTEGER NOT NULL REFERENCES serial.issuance (id) ON DELETE CASCADE,
+    subfield CHAR,
+    value TEXT
+);
 
-    SELECT * INTO layout FROM unapi.bre_output_layout WHERE name = format;
+CREATE OR REPLACE FUNCTION serial.materialize_holding_code() RETURNS TRIGGER
+AS $func$ 
+use strict;
 
-    IF layout.name IS NULL THEN
-        RETURN NULL::XML;
-    END IF;
+use MARC::Field;
+use JSON::XS;
 
-    SELECT * INTO xfrm FROM config.xml_transform WHERE name = layout.transform;
+if (not defined $_TD->{new}{holding_code}) {
+    elog(WARNING, 'NULL in "holding_code" column of serial.issuance allowed for now, but may not be useful');
+    return;
+}
 
-    SELECT * INTO me FROM biblio.record_entry WHERE id = obj_id;
+# Do nothing if holding_code has not changed...
 
-    -- grab SVF if we need them
-    IF ('mra' = ANY (includes)) THEN 
-        axml := unapi.mra(obj_id,NULL,NULL,NULL,NULL);
-    ELSE
-        axml := NULL::XML;
-    END IF;
+if ($_TD->{new}{holding_code} eq $_TD->{old}{holding_code}) {
+    # ... unless the following internal flag is set.
 
-    -- grab holdings if we need them
-    IF ('holdings_xml' = ANY (includes)) THEN 
-        hxml := unapi.holdings_xml(obj_id, ouid, org, depth, evergreen.array_remove_item_by_value(includes,'holdings_xml'), slimit, soffset, include_xmlns, pref_lib);
-    ELSE
-        hxml := NULL::XML;
-    END IF;
+    my $flag_rv = spi_exec_query(q{
+        SELECT * FROM config.internal_flag
+        WHERE name = 'serial.rematerialize_on_same_holding_code' AND enabled
+    }, 1);
+    return unless $flag_rv->{processed};
+}
 
 
-    -- generate our item node
+my $holding_code = (new JSON::XS)->decode($_TD->{new}{holding_code});
 
+my $field = new MARC::Field('999', @$holding_code); # tag doesnt matter
 
-    IF format = 'marcxml' THEN
-        tmp_xml := me.marc;
-        IF tmp_xml !~ E'<marc:' THEN -- If we're not using the prefixed namespace in this record, then remove all declarations of it
-           tmp_xml := REGEXP_REPLACE(tmp_xml, ' xmlns:marc="http://www.loc.gov/MARC21/slim"', '', 'g');
-        END IF; 
-    ELSE
-        tmp_xml := oils_xslt_process(me.marc, xfrm.xslt)::XML;
-    END IF;
+my $dstmt = spi_prepare(
+    'DELETE FROM serial.materialized_holding_code WHERE issuance = $1',
+    'INT'
+);
+spi_exec_prepared($dstmt, $_TD->{new}{id});
 
-    top_el := REGEXP_REPLACE(tmp_xml, E'^.*?<((?:\\S+:)?' || layout.holdings_element || ').*$', E'\\1');
+my $istmt = spi_prepare(
+    q{
+        INSERT INTO serial.materialized_holding_code (
+            issuance, subfield, value
+        ) VALUES ($1, $2, $3)
+    }, qw{INT CHAR TEXT}
+);
 
-    IF axml IS NOT NULL THEN 
-        tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', axml || '</' || top_el || E'>\\1');
-    END IF;
+foreach ($field->subfields) {
+    spi_exec_prepared(
+        $istmt,
+        $_TD->{new}{id},
+        $_->[0],
+        $_->[1]
+    );
+}
 
-    IF hxml IS NOT NULL THEN -- XXX how do we configure the holdings position?
-        tmp_xml := REGEXP_REPLACE(tmp_xml, '</' || top_el || '>(.*?)$', hxml || '</' || top_el || E'>\\1');
-    END IF;
+return;
 
-    IF ('bre.unapi' = ANY (includes)) THEN 
-        output := REGEXP_REPLACE(
-            tmp_xml,
-            '</' || top_el || '>(.*?)',
-            XMLELEMENT(
-                name abbr,
-                XMLATTRIBUTES(
-                    'http://www.w3.org/1999/xhtml' AS xmlns,
-                    'unapi-id' AS class,
-                    'tag:open-ils.org:U2 at bre/' || obj_id || '/' || org AS title
-                )
-            )::TEXT || '</' || top_el || E'>\\1'
-        );
-    ELSE
-        output := tmp_xml;
-    END IF;
+$func$ LANGUAGE 'plperlu';
 
-    output := REGEXP_REPLACE(output::TEXT,E'>\\s+<','><','gs')::XML;
-    RETURN output;
-END;
-$F$ LANGUAGE PLPGSQL STABLE;
+CREATE INDEX assist_holdings_display
+    ON serial.materialized_holding_code (issuance, subfield);
 
-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 at bre/' || $1 || '/' || $3 ELSE NULL END AS id
-                 ),
-                 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) 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), 0, 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;
+CREATE TRIGGER materialize_holding_code
+    AFTER INSERT OR UPDATE ON serial.issuance
+    FOR EACH ROW EXECUTE PROCEDURE serial.materialize_holding_code() ;
 
-CREATE OR REPLACE FUNCTION unapi.ssub ( 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 subscription,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at ssub/' || id AS id,
-                        'tag:open-ils.org:U2 at aou/' || owning_lib AS owning_lib,
-                        start_date AS start, end_date AS end, expected_date_offset
-                    ),
-                    CASE 
-                        WHEN ('sdist' = ANY ($4)) THEN
-                            XMLELEMENT( name distributions,
-                                (SELECT XMLAGG(sdist) FROM (
-                                    SELECT  unapi.sdist( id, 'xml', 'distribution', evergreen.array_remove_item_by_value($4,'ssub'), $5, $6, $7, $8, FALSE)
-                                      FROM  serial.distribution
-                                      WHERE subscription = ssub.id
-                                )x)
-                            )
-                        ELSE NULL
-                    END
-                )
-          FROM  serial.subscription ssub
-          WHERE id = $1
-          GROUP BY id, start_date, end_date, expected_date_offset, owning_lib;
-$F$ LANGUAGE SQL STABLE;
+-- starting here, we materialize all existing holding codes.
 
-CREATE OR REPLACE FUNCTION unapi.sdist ( 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 distribution,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at sdist/' || id AS id,
-            			'tag:open-ils.org:U2 at acn/' || receive_call_number AS receive_call_number,
-			            'tag:open-ils.org:U2 at acn/' || bind_call_number AS bind_call_number,
-                        unit_label_prefix, label, unit_label_suffix, summary_method
-                    ),
-                    unapi.aou( holding_lib, $2, 'holding_lib', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8),
-                    CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                    CASE 
-                        WHEN ('sstr' = ANY ($4)) THEN
-                            XMLELEMENT( name streams,
-                                (SELECT XMLAGG(sstr) FROM (
-                                    SELECT  unapi.sstr( id, 'xml', 'stream', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
-                                      FROM  serial.stream
-                                      WHERE distribution = sdist.id
-                                )x)
-                            )
-                        ELSE NULL
-                    END,
-                    XMLELEMENT( name summaries,
-                        CASE 
-                            WHEN ('sbsum' = ANY ($4)) THEN
-                                (SELECT XMLAGG(sbsum) FROM (
-                                    SELECT  unapi.sbsum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
-                                      FROM  serial.basic_summary
-                                      WHERE distribution = sdist.id
-                                )x)
-                            ELSE NULL
-                        END,
-                        CASE 
-                            WHEN ('sisum' = ANY ($4)) THEN
-                                (SELECT XMLAGG(sisum) FROM (
-                                    SELECT  unapi.sisum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
-                                      FROM  serial.index_summary
-                                      WHERE distribution = sdist.id
-                                )x)
-                            ELSE NULL
-                        END,
-                        CASE 
-                            WHEN ('sssum' = ANY ($4)) THEN
-                                (SELECT XMLAGG(sssum) FROM (
-                                    SELECT  unapi.sssum( id, 'xml', 'serial_summary', evergreen.array_remove_item_by_value($4,'sdist'), $5, $6, $7, $8, FALSE)
-                                      FROM  serial.supplement_summary
-                                      WHERE distribution = sdist.id
-                                )x)
-                            ELSE NULL
-                        END
-                    )
-                )
-          FROM  serial.distribution sdist
-          WHERE id = $1
-          GROUP BY id, label, unit_label_prefix, unit_label_suffix, holding_lib, summary_method, subscription, receive_call_number, bind_call_number;
-$F$ LANGUAGE SQL STABLE;
+UPDATE config.internal_flag
+    SET enabled = TRUE
+    WHERE name = 'serial.rematerialize_on_same_holding_code';
 
-CREATE OR REPLACE FUNCTION unapi.sstr ( 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 stream,
-                XMLATTRIBUTES(
-                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    'tag:open-ils.org:U2 at sstr/' || id AS id,
-                    routing_label
-                ),
-                CASE WHEN distribution IS NOT NULL AND ('sdist' = ANY ($4)) THEN unapi.sssum( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                CASE 
-                    WHEN ('sitem' = ANY ($4)) THEN
-                        XMLELEMENT( name items,
-                            (SELECT XMLAGG(sitem) FROM (
-                                SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'sstr'), $5, $6, $7, $8, FALSE)
-                                  FROM  serial.item
-                                  WHERE stream = sstr.id
-                            )x)
-                        )
-                    ELSE NULL
-                END
-            )
-      FROM  serial.stream sstr
-      WHERE id = $1
-      GROUP BY id, routing_label, distribution;
-$F$ LANGUAGE SQL STABLE;
+UPDATE serial.issuance SET holding_code = holding_code;
+
+UPDATE config.internal_flag
+    SET enabled = FALSE
+    WHERE name = 'serial.rematerialize_on_same_holding_code';
 
-CREATE OR REPLACE FUNCTION unapi.siss ( 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 issuance,
-                XMLATTRIBUTES(
-                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    'tag:open-ils.org:U2 at siss/' || id AS id,
-                    create_date, edit_date, label, date_published,
-                    holding_code, holding_type, holding_link_id
-                ),
-                CASE WHEN subscription IS NOT NULL AND ('ssub' = ANY ($4)) THEN unapi.ssub( subscription, 'xml', 'subscription', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                CASE 
-                    WHEN ('sitem' = ANY ($4)) THEN
-                        XMLELEMENT( name items,
-                            (SELECT XMLAGG(sitem) FROM (
-                                SELECT  unapi.sitem( id, 'xml', 'serial_item', evergreen.array_remove_item_by_value($4,'siss'), $5, $6, $7, $8, FALSE)
-                                  FROM  serial.item
-                                  WHERE issuance = sstr.id
-                            )x)
-                        )
-                    ELSE NULL
-                END
-            )
-      FROM  serial.issuance sstr
-      WHERE id = $1
-      GROUP BY id, create_date, edit_date, label, date_published, holding_code, holding_type, holding_link_id, subscription;
-$F$ LANGUAGE SQL STABLE;
+-- finish holding code materialization process
 
-CREATE OR REPLACE FUNCTION unapi.sitem ( 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 serial_item,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at sitem/' || id AS id,
-                        'tag:open-ils.org:U2 at siss/' || issuance AS issuance,
-                        date_expected, date_received
-                    ),
-                    CASE WHEN issuance IS NOT NULL AND ('siss' = ANY ($4)) THEN unapi.siss( issuance, $2, 'issuance', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                    CASE WHEN stream IS NOT NULL AND ('sstr' = ANY ($4)) THEN unapi.sstr( stream, $2, 'stream', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                    CASE WHEN unit IS NOT NULL AND ('sunit' = ANY ($4)) THEN unapi.sunit( unit, $2, 'serial_unit', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                    CASE WHEN uri IS NOT NULL AND ('auri' = ANY ($4)) THEN unapi.auri( uri, $2, 'uri', evergreen.array_remove_item_by_value($4,'sitem'), $5, $6, $7, $8, FALSE) ELSE NULL END
---                    XMLELEMENT( name notes,
---                        CASE 
---                            WHEN ('acpn' = ANY ($4)) THEN
---                                (SELECT XMLAGG(acpn) FROM (
---                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8)
---                                      FROM  asset.copy_note
---                                      WHERE owning_copy = cp.id AND pub
---                                )x)
---                            ELSE NULL
---                        END
---                    )
-                )
-          FROM  serial.item sitem
-          WHERE id = $1;
-$F$ LANGUAGE SQL STABLE;
+-- fix up missing holding_code fields from serial.issuance
+UPDATE serial.issuance siss
+    SET holding_type = scap.type
+    FROM serial.caption_and_pattern scap
+    WHERE scap.id = siss.caption_and_pattern AND siss.holding_type IS NULL;
 
 
-CREATE OR REPLACE FUNCTION unapi.sssum ( 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 serial_summary,
-                XMLATTRIBUTES(
-                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    'tag:open-ils.org:U2 at sbsum/' || id AS id,
-                    'sssum' AS type, generated_coverage, textual_holdings, show_generated
-                ),
-                CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
-            )
-      FROM  serial.supplement_summary ssum
-      WHERE id = $1
-      GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
-$F$ LANGUAGE SQL STABLE;
+-- Evergreen DB patch 0701.schema.patron_stat_category_enhancements.sql
+--
+-- Enables users to set patron statistical categories as required,
+-- whether or not users can input free text for the category value.
+-- Enables administrators to set an entry as the default for any
+-- given patron statistical category and org unit.
+--
 
-CREATE OR REPLACE FUNCTION unapi.sbsum ( 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 serial_summary,
-                XMLATTRIBUTES(
-                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    'tag:open-ils.org:U2 at sbsum/' || id AS id,
-                    'sbsum' AS type, generated_coverage, textual_holdings, show_generated
-                ),
-                CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
-            )
-      FROM  serial.basic_summary ssum
-      WHERE id = $1
-      GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
-$F$ LANGUAGE SQL STABLE;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0701', :eg_version);
 
-CREATE OR REPLACE FUNCTION unapi.sisum ( 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 serial_summary,
-                XMLATTRIBUTES(
-                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    'tag:open-ils.org:U2 at sbsum/' || id AS id,
-                    'sisum' AS type, generated_coverage, textual_holdings, show_generated
-                ),
-                CASE WHEN ('sdist' = ANY ($4)) THEN unapi.sdist( distribution, 'xml', 'distribtion', evergreen.array_remove_item_by_value($4,'ssum'), $5, $6, $7, $8, FALSE) ELSE NULL END
-            )
-      FROM  serial.index_summary ssum
-      WHERE id = $1
-      GROUP BY id, generated_coverage, textual_holdings, distribution, show_generated;
-$F$ LANGUAGE SQL STABLE;
+-- New table
 
+CREATE TABLE actor.stat_cat_entry_default (
+    id              SERIAL  PRIMARY KEY,
+    stat_cat_entry  INT     NOT NULL REFERENCES actor.stat_cat_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    stat_cat        INT     NOT NULL REFERENCES actor.stat_cat (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    owner           INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    CONSTRAINT sced_once_per_owner UNIQUE (stat_cat,owner)
+);
 
-CREATE OR REPLACE FUNCTION unapi.aou ( 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$
-DECLARE
-    output XML;
-BEGIN
-    IF ename = 'circlib' THEN
-        SELECT  XMLELEMENT(
-                    name circlib,
-                    XMLATTRIBUTES(
-                        'http://open-ils.org/spec/actors/v1' AS xmlns,
-                        id AS ident
-                    ),
-                    name
-                ) INTO output
-          FROM  actor.org_unit aou
-          WHERE id = obj_id;
-    ELSE
-        EXECUTE $$SELECT  XMLELEMENT(
-                    name $$ || ename || $$,
-                    XMLATTRIBUTES(
-                        'http://open-ils.org/spec/actors/v1' AS xmlns,
-                        'tag:open-ils.org:U2 at aou/' || id AS id,
-                        shortname, name, opac_visible
-                    )
-                )
-          FROM  actor.org_unit aou
-         WHERE id = $1 $$ INTO output USING obj_id;
-    END IF;
+COMMENT ON TABLE actor.stat_cat_entry_default IS $$
+User Statistical Category Default Entry
 
-    RETURN output;
+A library may choose one of the stat_cat entries to be the
+default entry.
+$$;
 
-END;
-$F$ LANGUAGE PLPGSQL STABLE;
+-- Add columns to existing tables
 
-CREATE OR REPLACE FUNCTION unapi.acl ( 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 location,
-                XMLATTRIBUTES(
-                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    id AS ident,
-                    holdable,
-                    opac_visible,
-                    label_prefix AS prefix,
-                    label_suffix AS suffix
-                ),
-                name
-            )
-      FROM  asset.copy_location
-      WHERE id = $1;
-$F$ LANGUAGE SQL STABLE;
+-- Patron stat cat required column
+ALTER TABLE actor.stat_cat
+    ADD COLUMN required BOOL NOT NULL DEFAULT FALSE;
 
-CREATE OR REPLACE FUNCTION unapi.ccs ( 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 status,
-                XMLATTRIBUTES(
-                    CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    id AS ident,
-                    holdable,
-                    opac_visible
-                ),
-                name
-            )
-      FROM  config.copy_status
-      WHERE id = $1;
-$F$ LANGUAGE SQL STABLE;
+-- Patron stat cat allow_freetext column
+ALTER TABLE actor.stat_cat
+    ADD COLUMN allow_freetext BOOL NOT NULL DEFAULT TRUE;
 
-CREATE OR REPLACE FUNCTION unapi.acpn ( 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 copy_note,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        create_date AS date,
-                        title
-                    ),
-                    value
-                )
-          FROM  asset.copy_note
-          WHERE id = $1;
-$F$ LANGUAGE SQL STABLE;
+-- Add permissions
 
-CREATE OR REPLACE FUNCTION unapi.ascecm ( 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 statcat,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        sc.name,
-                        sc.opac_visible
-                    ),
-                    asce.value
-                )
-          FROM  asset.stat_cat_entry asce
-                JOIN asset.stat_cat sc ON (sc.id = asce.stat_cat)
-          WHERE asce.id = $1;
-$F$ LANGUAGE SQL STABLE;
+INSERT INTO permission.perm_list ( id, code, description ) VALUES
+    ( 525, 'CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 525, 
+        'User may set a default entry in a patron statistical category', 'ppl', 'description' )),
+    ( 526, 'UPDATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 526, 
+        'User may reset a default entry in a patron statistical category', 'ppl', 'description' )),
+    ( 527, 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 527, 
+        'User may unset a default entry in a patron statistical category', 'ppl', 'description' ));
 
-CREATE OR REPLACE FUNCTION unapi.bmp ( 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 monograph_part,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at bmp/' || id AS id,
-                        id AS ident,
-                        label,
-                        label_sortkey,
-                        'tag:open-ils.org:U2 at bre/' || record AS record
-                    ),
-                    CASE 
-                        WHEN ('acp' = ANY ($4)) THEN
-                            XMLELEMENT( name copies,
-                                (SELECT XMLAGG(acp) FROM (
-                                    SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE)
-                                      FROM  asset.copy cp
-                                            JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id)
-                                      WHERE cpm.part = $1
-                                          AND cp.deleted IS FALSE
-                                      ORDER BY COALESCE(cp.copy_number,0), cp.barcode
-                                      LIMIT ($7 -> 'acp')::INT
-                                      OFFSET ($8 -> 'acp')::INT
+INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
+    SELECT
+        pgt.id, perm.id, aout.depth, TRUE
+    FROM
+        permission.grp_tree pgt,
+        permission.perm_list perm,
+        actor.org_unit_type aout
+    WHERE
+        pgt.name = 'Circulation Administrator' AND
+        aout.name = 'System' AND
+        perm.code IN ('CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT');
 
-                                )x)
-                            )
-                        ELSE NULL
-                    END,
-                    CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8, FALSE) ELSE NULL END
-                )
-          FROM  biblio.monograph_part
-          WHERE id = $1
-          GROUP BY id, label, label_sortkey, record;
-$F$ LANGUAGE SQL STABLE;
 
-CREATE OR REPLACE FUNCTION unapi.acp ( 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 copy,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at acp/' || id AS id, id AS copy_id,
-                        create_date, edit_date, copy_number, circulate, deposit,
-                        ref, holdable, deleted, deposit_amount, price, barcode,
-                        circ_modifier, circ_as_type, opac_visible, age_protect
-                    ),
-                    unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
-                    unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE),
-                    unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
-                    unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8),
-                    CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                    CASE 
-                        WHEN ('acpn' = ANY ($4)) THEN
-                            XMLELEMENT( name copy_notes,
-                                (SELECT XMLAGG(acpn) FROM (
-                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
-                                      FROM  asset.copy_note
-                                      WHERE owning_copy = cp.id AND pub
-                                )x)
-                            )
-                        ELSE NULL
-                    END,
-                    CASE 
-                        WHEN ('ascecm' = ANY ($4)) THEN
-                            XMLELEMENT( name statcats,
-                                (SELECT XMLAGG(ascecm) FROM (
-                                    SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
-                                      FROM  asset.stat_cat_entry_copy_map
-                                      WHERE owning_copy = cp.id
-                                )x)
-                            )
-                        ELSE NULL
-                    END,
-                    CASE
-                        WHEN ('bre' = ANY ($4)) THEN
-                            XMLELEMENT( name foreign_records,
-                                (SELECT XMLAGG(bre) FROM (
-                                    SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
-                                      FROM  biblio.peer_bib_copy_map
-                                      WHERE target_copy = cp.id
-                                )x)
+SELECT evergreen.upgrade_deps_block_check('0702', :eg_version);
 
-                            )
-                        ELSE NULL
-                    END,
-                    CASE 
-                        WHEN ('bmp' = ANY ($4)) THEN
-                            XMLELEMENT( name monograph_parts,
-                                (SELECT XMLAGG(bmp) FROM (
-                                    SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
-                                      FROM  asset.copy_part_map
-                                      WHERE target_copy = cp.id
-                                )x)
-                            )
-                        ELSE NULL
-                    END,
-                    CASE 
-                        WHEN ('circ' = ANY ($4)) THEN
-                            XMLELEMENT( name current_circulation,
-                                (SELECT XMLAGG(circ) FROM (
-                                    SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
-                                      FROM  action.circulation
-                                      WHERE target_copy = cp.id
-                                            AND checkin_time IS NULL
-                                )x)
-                            )
-                        ELSE NULL
-                    END
-                )
-          FROM  asset.copy cp
-          WHERE id = $1
-              AND cp.deleted IS FALSE
-          GROUP BY id, status, location, circ_lib, call_number, create_date,
-              edit_date, copy_number, circulate, deposit, ref, holdable,
-              deleted, deposit_amount, price, barcode, circ_modifier,
-              circ_as_type, opac_visible, age_protect;
-$F$ LANGUAGE SQL STABLE;
+INSERT INTO config.global_flag (name, enabled, label) 
+    VALUES (
+        'opac.org_unit.non_inherited_visibility',
+        FALSE,
+        oils_i18n_gettext(
+            'opac.org_unit.non_inherited_visibility',
+            'Org Units Do Not Inherit Visibility',
+            'cgf',
+            'label'
+        )
+    );
 
-CREATE OR REPLACE FUNCTION unapi.sunit ( 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 serial_unit,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at acp/' || id AS id, id AS copy_id,
-                        create_date, edit_date, copy_number, circulate, deposit,
-                        ref, holdable, deleted, deposit_amount, price, barcode,
-                        circ_modifier, circ_as_type, opac_visible, age_protect,
-                        status_changed_time, floating, mint_condition,
-                        detailed_contents, sort_key, summary_contents, cost 
-                    ),
-                    unapi.ccs( status, $2, 'status', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
-                    unapi.acl( location, $2, 'location', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE),
-                    unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
-                    unapi.aou( circ_lib, $2, 'circlib', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8),
-                    CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-                    XMLELEMENT( name copy_notes,
-                        CASE 
-                            WHEN ('acpn' = ANY ($4)) THEN
-                                (SELECT XMLAGG(acpn) FROM (
-                                    SELECT  unapi.acpn( id, 'xml', 'copy_note', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($4,'acp'),'sunit'), $5, $6, $7, $8, FALSE)
-                                      FROM  asset.copy_note
-                                      WHERE owning_copy = cp.id AND pub
-                                )x)
-                            ELSE NULL
-                        END
-                    ),
-                    XMLELEMENT( name statcats,
-                        CASE 
-                            WHEN ('ascecm' = ANY ($4)) THEN
-                                (SELECT XMLAGG(ascecm) FROM (
-                                    SELECT  unapi.ascecm( stat_cat_entry, 'xml', 'statcat', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
-                                      FROM  asset.stat_cat_entry_copy_map
-                                      WHERE owning_copy = cp.id
-                                )x)
-                            ELSE NULL
-                        END
-                    ),
-                    XMLELEMENT( name foreign_records,
-                        CASE
-                            WHEN ('bre' = ANY ($4)) THEN
-                                (SELECT XMLAGG(bre) FROM (
-                                    SELECT  unapi.bre(peer_record,'marcxml','record','{}'::TEXT[], $5, $6, $7, $8, FALSE)
-                                      FROM  biblio.peer_bib_copy_map
-                                      WHERE target_copy = cp.id
-                                )x)
-                            ELSE NULL
-                        END
-                    ),
-                    CASE 
-                        WHEN ('bmp' = ANY ($4)) THEN
-                            XMLELEMENT( name monograph_parts,
-                                (SELECT XMLAGG(bmp) FROM (
-                                    SELECT  unapi.bmp( part, 'xml', 'monograph_part', evergreen.array_remove_item_by_value($4,'acp'), $5, $6, $7, $8, FALSE)
-                                      FROM  asset.copy_part_map
-                                      WHERE target_copy = cp.id
-                                )x)
-                            )
-                        ELSE NULL
-                    END,
-                    CASE 
-                        WHEN ('circ' = ANY ($4)) THEN
-                            XMLELEMENT( name current_circulation,
-                                (SELECT XMLAGG(circ) FROM (
-                                    SELECT  unapi.circ( id, 'xml', 'circ', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE)
-                                      FROM  action.circulation
-                                      WHERE target_copy = cp.id
-                                            AND checkin_time IS NULL
-                                )x)
-                            )
-                        ELSE NULL
-                    END
-                )
-          FROM  serial.unit cp
-          WHERE id = $1
-              AND cp.deleted IS FALSE
-          GROUP BY id, status, location, circ_lib, call_number, create_date,
-              edit_date, copy_number, circulate, floating, mint_condition,
-              deposit, ref, holdable, deleted, deposit_amount, price,
-              barcode, circ_modifier, circ_as_type, opac_visible,
-              status_changed_time, detailed_contents, sort_key,
-              summary_contents, cost, age_protect;
-$F$ LANGUAGE SQL STABLE;
+CREATE TYPE actor.org_unit_custom_tree_purpose AS ENUM ('opac');
 
-CREATE OR REPLACE FUNCTION unapi.acn ( 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 volume,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at acn/' || acn.id AS id,
-                        acn.id AS vol_id, o.shortname AS lib,
-                        o.opac_visible AS opac_visible,
-                        deleted, label, label_sortkey, label_class, record
-                    ),
-                    unapi.aou( owning_lib, $2, 'owning_lib', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8),
-                    CASE 
-                        WHEN ('acp' = ANY ($4)) THEN
-                            CASE WHEN $6 IS NOT NULL THEN
-                                XMLELEMENT( name copies,
-                                    (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
-                                        SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
-                                            evergreen.rank_cp_status(cp.status) AS rank_avail
-                                          FROM  asset.copy cp
-                                                JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5), $6) aoud ON (cp.circ_lib = aoud.id)
-                                          WHERE cp.call_number = acn.id
-                                              AND cp.deleted IS FALSE
-                                          ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
-                                          LIMIT ($7 -> 'acp')::INT
-                                          OFFSET ($8 -> 'acp')::INT
-                                    )x)
-                                )
-                            ELSE
-                                XMLELEMENT( name copies,
-                                    (SELECT XMLAGG(acp ORDER BY rank_avail) FROM (
-                                        SELECT  unapi.acp( cp.id, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
-                                            evergreen.rank_cp_status(cp.status) AS rank_avail
-                                          FROM  asset.copy cp
-                                                JOIN actor.org_unit_descendants( (SELECT id FROM actor.org_unit WHERE shortname = $5) ) aoud ON (cp.circ_lib = aoud.id)
-                                          WHERE cp.call_number = acn.id
-                                              AND cp.deleted IS FALSE
-                                          ORDER BY rank_avail, COALESCE(cp.copy_number,0), cp.barcode
-                                          LIMIT ($7 -> 'acp')::INT
-                                          OFFSET ($8 -> 'acp')::INT
-                                    )x)
-                                )
-                            END
-                        ELSE NULL
-                    END,
-                    XMLELEMENT(
-                        name uris,
-                        (SELECT XMLAGG(auri) FROM (SELECT unapi.auri(uri,'xml','uri', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE call_number = acn.id)x)
-                    ),
-                    unapi.acnp( acn.prefix, 'marcxml', 'prefix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
-                    unapi.acns( acn.suffix, 'marcxml', 'suffix', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE),
-                    CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( acn.record, 'marcxml', 'record', evergreen.array_remove_item_by_value($4,'acn'), $5, $6, $7, $8, FALSE) ELSE NULL END
-                ) AS x
-          FROM  asset.call_number acn
-                JOIN actor.org_unit o ON (o.id = acn.owning_lib)
-          WHERE acn.id = $1
-              AND acn.deleted IS FALSE
-          GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix;
-$F$ LANGUAGE SQL STABLE;
+CREATE TABLE actor.org_unit_custom_tree (
+    id              SERIAL  PRIMARY KEY,
+    active          BOOLEAN DEFAULT FALSE,
+    purpose         actor.org_unit_custom_tree_purpose NOT NULL DEFAULT 'opac' UNIQUE
+);
+
+CREATE TABLE actor.org_unit_custom_tree_node (
+    id              SERIAL  PRIMARY KEY,
+    tree            INTEGER REFERENCES actor.org_unit_custom_tree (id) DEFERRABLE INITIALLY DEFERRED,
+	org_unit        INTEGER NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
+	parent_node     INTEGER REFERENCES actor.org_unit_custom_tree_node (id) DEFERRABLE INITIALLY DEFERRED,
+    sibling_order   INTEGER NOT NULL DEFAULT 0,
+    CONSTRAINT aouctn_once_per_org UNIQUE (tree, org_unit)
+);
+    
 
-CREATE OR REPLACE FUNCTION unapi.acnp ( 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 call_number_prefix,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        id AS ident,
-                        label,
-                        'tag:open-ils.org:U2 at aou/' || owning_lib AS owning_lib,
-                        label_sortkey
-                    )
-                )
-          FROM  asset.call_number_prefix
-          WHERE id = $1;
-$F$ LANGUAGE SQL STABLE;
+/* UNDO
+BEGIN;
+DELETE FROM config.global_flag WHERE name = 'opac.org_unit.non_inheritied_visibility';
+DROP TABLE actor.org_unit_custom_tree_node;
+DROP TABLE actor.org_unit_custom_tree;
+DROP TYPE actor.org_unit_custom_tree_purpose;
+COMMIT;
+*/
 
-CREATE OR REPLACE FUNCTION unapi.acns ( 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 call_number_suffix,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        id AS ident,
-                        label,
-                        'tag:open-ils.org:U2 at aou/' || owning_lib AS owning_lib,
-                        label_sortkey
-                    )
-                )
-          FROM  asset.call_number_suffix
-          WHERE id = $1;
-$F$ LANGUAGE SQL STABLE;
+-- Evergreen DB patch 0704.schema.query_parser_fts.sql
+--
+-- Add pref_ou query filter for preferred library searching
+--
 
-CREATE OR REPLACE FUNCTION unapi.auri ( 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 uri,
-                    XMLATTRIBUTES(
-                        CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                        'tag:open-ils.org:U2 at auri/' || uri.id AS id,
-                        use_restriction,
-                        href,
-                        label
-                    ),
-                    CASE 
-                        WHEN ('acn' = ANY ($4)) THEN
-                            XMLELEMENT( name copies,
-                                (SELECT XMLAGG(acn) FROM (SELECT unapi.acn( call_number, 'xml', 'copy', evergreen.array_remove_item_by_value($4,'auri'), $5, $6, $7, $8, FALSE) FROM asset.uri_call_number_map WHERE uri = uri.id)x)
-                            )
-                        ELSE NULL
-                    END
-                ) AS x
-          FROM  asset.uri uri
-          WHERE uri.id = $1
-          GROUP BY uri.id, use_restriction, href, label;
-$F$ LANGUAGE SQL STABLE;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0704', :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 at mra/' || mra.id AS id,
-                        'tag:open-ils.org:U2 at bre/' || mra.id AS record
-                    ),
-                    (SELECT XMLAGG(foo.y)
-                      FROM (SELECT XMLELEMENT(
-                                name field,
-                                XMLATTRIBUTES(
-                                    key AS name,
-                                    cvm.value AS "coded-value",
-                                    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;
+-- Create the new 11-parameter function, featuring param_pref_ou
+CREATE OR REPLACE FUNCTION search.query_parser_fts (
 
-CREATE OR REPLACE FUNCTION unapi.circ (obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT DEFAULT '-', depth INT DEFAULT NULL, slimit HSTORE DEFAULT NULL, soffset HSTORE DEFAULT NULL, include_xmlns BOOL DEFAULT TRUE ) RETURNS XML AS $F$
-    SELECT XMLELEMENT(
-        name circ,
-        XMLATTRIBUTES(
-            CASE WHEN $9 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-            'tag:open-ils.org:U2 at circ/' || id AS id,
-            xact_start,
-            due_date
-        ),
-        CASE WHEN ('aou' = ANY ($4)) THEN unapi.aou( circ_lib, $2, 'circ_lib', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END,
-        CASE WHEN ('acp' = ANY ($4)) THEN unapi.acp( circ_lib, $2, 'target_copy', evergreen.array_remove_item_by_value($4,'circ'), $5, $6, $7, $8, FALSE) ELSE NULL END
-    )
-    FROM action.circulation
-    WHERE id = $1;
-$F$ LANGUAGE SQL STABLE;
+    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,
+    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[];
 
- -- Some test queries
+    check_limit         INT;
+    core_limit          INT;
+    core_offset         INT;
+    tmp_int             INT;
 
-SELECT unapi.memoize( 'bre', 1,'mods32','','{holdings_xml,acp}'::TEXT[], 'SYS1');
-SELECT unapi.memoize( 'bre', 1,'marcxml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
-SELECT unapi.memoize( 'bre', 1,'holdings_xml','','{holdings_xml,acp}'::TEXT[], 'SYS1');
+    core_result         RECORD;
+    core_cursor         REFCURSOR;
+    core_rel_query      TEXT;
 
-SELECT unapi.biblio_record_entry_feed('{1}'::BIGINT[],'mods32','{holdings_xml,acp}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://c64/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
+    total_count         INT := 0;
+    check_count         INT := 0;
+    deleted_count       INT := 0;
+    visible_count       INT := 0;
+    excluded_count      INT := 0;
 
-SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
-EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
-EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'marcxml','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
-EXPLAIN ANALYZE SELECT unapi.biblio_record_entry_feed('{7209,7394}'::BIGINT[],'mods32','{holdings_xml}'::TEXT[],'SYS1',NULL,'acn=>1',NULL, NULL,NULL,NULL,NULL,'http://fulfillment2.esilibrary.com/opac/extras/unapi', '<totalResults xmlns="http://a9.com/-/spec/opensearch/1.1/">2</totalResults><startIndex xmlns="http://a9.com/-/spec/opensearch/1.1/">1</startIndex><itemsPerPage xmlns="http://a9.com/-/spec/opensearch/1.1/">10</itemsPerPage>');
+BEGIN
 
-SELECT unapi.biblio_record_entry_feed('{216}'::BIGINT[],'marcxml','{}'::TEXT[], 'BR1');
-EXPLAIN ANALYZE SELECT unapi.bre(216,'marcxml','record','{holdings_xml,bre.unapi}'::TEXT[], 'BR1');
-EXPLAIN ANALYZE SELECT unapi.bre(216,'holdings_xml','record','{}'::TEXT[], 'BR1');
-EXPLAIN ANALYZE SELECT unapi.holdings_xml(216,4,'BR1',2,'{bre}'::TEXT[]);
-EXPLAIN ANALYZE SELECT unapi.bre(216,'mods32','record','{}'::TEXT[], 'BR1');
+    check_limit := COALESCE( param_check, 1000 );
+    core_limit  := COALESCE( param_limit, 25000 );
+    core_offset := COALESCE( param_offset, 0 );
 
--- Limit to 5 call numbers, 5 copies, with a preferred library of 4 (BR1), in SYS2 at a depth of 0
-EXPLAIN ANALYZE SELECT unapi.bre(36,'marcxml','record','{holdings_xml,mra,acp,acnp,acns,bmp}','SYS2',0,'acn=>5,acp=>5',NULL,TRUE,4);
+    -- core_skip_chk := COALESCE( param_skip_chk, 1 );
 
-*/
+    IF param_search_ou > 0 THEN
+        IF param_depth IS NOT NULL THEN
+            SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
+        ELSE
+            SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
+        END IF;
 
+        SELECT array_accum(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
 
-SELECT evergreen.upgrade_deps_block_check('0691', :eg_version);
+    ELSIF param_search_ou < 0 THEN
+        SELECT array_accum(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
 
-CREATE INDEX poi_po_idx ON acq.po_item (purchase_order);
+        FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
+            SELECT array_accum(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
+            luri_org_list := luri_org_list || tmp_int_list;
+        END LOOP;
 
-CREATE INDEX ie_inv_idx on acq.invoice_entry (invoice);
-CREATE INDEX ie_po_idx on acq.invoice_entry (purchase_order);
-CREATE INDEX ie_li_idx on acq.invoice_entry (lineitem);
+        SELECT array_accum(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
 
-CREATE INDEX ii_inv_idx on acq.invoice_item (invoice);
-CREATE INDEX ii_po_idx on acq.invoice_item (purchase_order);
-CREATE INDEX ii_poi_idx on acq.invoice_item (po_item);
+    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_accum(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;
 
-SELECT evergreen.upgrade_deps_block_check('0692', :eg_version);
+    OPEN core_cursor FOR EXECUTE param_query;
 
-INSERT INTO config.org_unit_setting_type
-    (name, label, description, grp, datatype)
-    VALUES (
-        'circ.fines.charge_when_closed',
-         oils_i18n_gettext(
-            'circ.fines.charge_when_closed',
-            'Charge fines on overdue circulations when closed',
-            'coust',
-            'label'
-        ),
-        oils_i18n_gettext(
-            'circ.fines.charge_when_closed',
-            'Normally, fines are not charged when a library is closed.  When set to True, fines will be charged during scheduled closings and normal weekly closed days.',
-            'coust',
-            'description'
-        ),
-        'circ',
-        'bool'
-    );
+    LOOP
 
-SELECT evergreen.upgrade_deps_block_check('0694', :eg_version);
+        FETCH core_cursor INTO core_result;
+        EXIT WHEN NOT FOUND;
+        EXIT WHEN total_count >= core_limit;
 
-INSERT into config.org_unit_setting_type
-( name, grp, label, description, datatype, fm_class ) VALUES
+        total_count := total_count + 1;
 
-( 'ui.patron.edit.au.prefix.require', 'gui',
-    oils_i18n_gettext('ui.patron.edit.au.prefix.require',
-        'Require prefix field on patron registration',
-        'coust', 'label'),
-    oils_i18n_gettext('ui.patron.edit.au.prefix.require',
-        'The prefix field will be required on the patron registration screen.',
-        'coust', 'description'),
-    'bool', null)
-	
-,( 'ui.patron.edit.au.prefix.show', 'gui',
-    oils_i18n_gettext('ui.patron.edit.au.prefix.show',
-        'Show prefix field on patron registration',
-        'coust', 'label'),
-    oils_i18n_gettext('ui.patron.edit.au.prefix.show',
-        'The prefix 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', null)
+        CONTINUE WHEN total_count NOT BETWEEN  core_offset + 1 AND check_limit + core_offset;
+
+        check_count := check_count + 1;
+
+        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 ) );
 
-,( 'ui.patron.edit.au.prefix.suggest', 'gui',
-    oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
-        'Suggest prefix field on patron registration',
-        'coust', 'label'),
-    oils_i18n_gettext('ui.patron.edit.au.prefix.suggest',
-        'The prefix 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', null)
-;		
+        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;
 
--- Evergreen DB patch 0695.schema.custom_toolbars.sql
---
--- FIXME: insert description of change, if needed
---
+            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;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0695', :eg_version);
+            IF tmp_int = 1 THEN
+                current_res.record = core_result.records[1];
+            ELSE
+                current_res.record = NULL;
+            END IF;
 
-CREATE TABLE actor.toolbar (
-    id          BIGSERIAL   PRIMARY KEY,
-    ws          INT         REFERENCES actor.workstation (id) ON DELETE CASCADE,
-    org         INT         REFERENCES actor.org_unit (id) ON DELETE CASCADE,
-    usr         INT         REFERENCES actor.usr (id) ON DELETE CASCADE,
-    label       TEXT        NOT NULL,
-    layout      TEXT        NOT NULL,
-    CONSTRAINT only_one_type CHECK (
-        (ws IS NOT NULL AND COALESCE(org,usr) IS NULL) OR
-        (org IS NOT NULL AND COALESCE(ws,usr) IS NULL) OR
-        (usr IS NOT NULL AND COALESCE(org,ws) IS NULL)
-    ),
-    CONSTRAINT layout_must_be_json CHECK ( is_json(layout) )
-);
-CREATE UNIQUE INDEX label_once_per_ws ON actor.toolbar (ws, label) WHERE ws IS NOT NULL;
-CREATE UNIQUE INDEX label_once_per_org ON actor.toolbar (org, label) WHERE org IS NOT NULL;
-CREATE UNIQUE INDEX label_once_per_usr ON actor.toolbar (usr, label) WHERE usr IS NOT NULL;
+            RETURN NEXT current_res;
 
--- this one unrelated to toolbars but is a gap in the upgrade scripts
-INSERT INTO permission.perm_list ( id, code, description )
-    SELECT
-        522,
-        'IMPORT_AUTHORITY_MARC',
-        oils_i18n_gettext(
-            522,
-            'Allows a user to create new authority records',
-            'ppl',
-            'description'
-        )
-    WHERE NOT EXISTS (
-        SELECT 1
-        FROM permission.perm_list
-        WHERE
-            id = 522
-    );
+            CONTINUE;
+        END IF;
 
-INSERT INTO permission.perm_list ( id, code, description ) VALUES (
-    523,
-    'ADMIN_TOOLBAR',
-    oils_i18n_gettext(
-        523,
-        'Allows a user to create, edit, and delete custom toolbars',
-        'ppl',
-        'description'
-    )
-);
+        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;
 
--- Don't want to assume stock perm groups in an upgrade script, but here for ease of testing
--- INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) SELECT pgt.id, perm.id, aout.depth, FALSE FROM permission.grp_tree pgt, permission.perm_list perm, actor.org_unit_type aout WHERE pgt.name = 'Staff' AND aout.name = 'Branch' AND perm.code = 'ADMIN_TOOLBAR';
+        IF FOUND THEN
+            -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
+            visible_count := visible_count + 1;
 
-INSERT INTO actor.toolbar(org,label,layout) VALUES
-    ( 1, 'circ', '["circ_checkout","circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","patron_search","patron_register","toolbarspacer.3","hotkeys_toggle"]' ),
-    ( 1, 'cat', '["circ_checkin","toolbarseparator.1","search_opac","copy_status","toolbarseparator.2","create_marc","authority_manage","retrieve_last_record","toolbarspacer.3","hotkeys_toggle"]' );
+            current_res.id = core_result.id;
+            current_res.rel = core_result.rel;
 
--- delete from permission.grp_perm_map where perm in (select id from permission.perm_list where code ~ 'TOOLBAR'); delete from permission.perm_list where code ~ 'TOOLBAR'; drop table actor.toolbar ;
+            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;
 
--- Evergreen DB patch 0696.no_plperl.sql
---
--- FIXME: insert description of change, if needed
---
+            IF tmp_int = 1 THEN
+                current_res.record = core_result.records[1];
+            ELSE
+                current_res.record = NULL;
+            END IF;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0696', :eg_version);
+            RETURN NEXT current_res;
 
--- Re-create these as plperlu instead of plperl
-CREATE OR REPLACE FUNCTION auditor.set_audit_info(INT, INT) RETURNS VOID AS $$
-    $_SHARED{"eg_audit_user"} = $_[0];
-    $_SHARED{"eg_audit_ws"} = $_[1];
-$$ LANGUAGE plperlu;
+            CONTINUE;
+        END IF;
 
-CREATE OR REPLACE FUNCTION auditor.get_audit_info() RETURNS TABLE (eg_user INT, eg_ws INT) AS $$
-    return [{eg_user => $_SHARED{"eg_audit_user"}, eg_ws => $_SHARED{"eg_audit_ws"}}];
-$$ LANGUAGE plperlu;
+        IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
 
-CREATE OR REPLACE FUNCTION auditor.clear_audit_info() RETURNS VOID AS $$
-    delete($_SHARED{"eg_audit_user"});
-    delete($_SHARED{"eg_audit_ws"});
-$$ LANGUAGE plperlu;
+            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;
 
--- And remove the language so that we don't use it later.
-DROP LANGUAGE plperl;
+            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;
 
--- Evergreen DB patch 0697.data.place_currently_unfillable_hold.sql
---
--- FIXME: insert description of change, if needed
---
+                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;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0697', :eg_version);
+        END IF;
 
--- FIXME: add/check SQL statements to perform the upgrade
-INSERT INTO permission.perm_list ( id, code, description ) VALUES
- ( 524, 'PLACE_UNFILLABLE_HOLD', oils_i18n_gettext( 524,
-    'Allows a user to place a hold that cannot currently be filled.', 'ppl', 'description' ));
+        IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
 
--- Evergreen DB patch 0698.hold_default_pickup.sql
---
--- FIXME: insert description of change, if needed
---
+            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;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0698', :eg_version);
+            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;
 
-INSERT INTO config.usr_setting_type (name,opac_visible,label,description,datatype)
-    VALUES ('opac.default_pickup_location', TRUE, 'Default Hold Pickup Location', 'Default location for holds pickup', 'integer');
+                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;
 
-SELECT evergreen.upgrade_deps_block_check('0699', :eg_version);
+        END IF;
 
-INSERT INTO config.org_unit_setting_type ( name, label, description, datatype, grp )
-    VALUES (
-        'ui.hide_copy_editor_fields',
-        oils_i18n_gettext(
-            'ui.hide_copy_editor_fields',
-            'GUI: Hide these fields within the Item Attribute Editor',
-            'coust',
-            'label'
-        ),
-        oils_i18n_gettext(
-            'ui.hide_copy_editor_fields',
-            'This setting may be best maintained with the dedicated configuration'
-            || ' interface within the Item Attribute Editor.  However, here it'
-            || ' shows up as comma separated list of field identifiers to hide.',
-            'coust',
-            'description'
-        ),
-        'array',
-        'gui'
-    );
+        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;
 
-SELECT evergreen.upgrade_deps_block_check('0700', :eg_version);
-SELECT evergreen.upgrade_deps_block_check('0706', :eg_version);
+            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;
 
--- This throws away data, but only data that causes breakage anyway.
-UPDATE serial.issuance SET holding_code = NULL WHERE NOT is_json(holding_code);
+                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 we don't do this, we have unprocessed triggers and we can't alter the table
-SET CONSTRAINTS serial.issuance_caption_and_pattern_fkey IMMEDIATE;
+            IF NOT FOUND THEN
 
-ALTER TABLE serial.issuance ADD CHECK (holding_code IS NULL OR is_json(holding_code));
+                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;
 
-INSERT INTO config.internal_flag (name, value, enabled) VALUES (
-    'serial.rematerialize_on_same_holding_code', NULL, FALSE
-);
+                IF NOT FOUND THEN
 
-INSERT INTO config.org_unit_setting_type (
-    name, label, grp, description, datatype
-) VALUES (
-    'serial.default_display_grouping',
-    'Default display grouping for serials distributions presented in the OPAC.',
-    'serial',
-    'Default display grouping for serials distributions presented in the OPAC. This can be "enum" or "chron".',
-    'string'
-);
+                    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;
 
-ALTER TABLE serial.distribution
-    ADD COLUMN display_grouping TEXT NOT NULL DEFAULT 'chron'
-        CHECK (display_grouping IN ('enum', 'chron'));
+                    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;
 
--- why didn't we just make one summary table in the first place?
-CREATE VIEW serial.any_summary AS
-    SELECT
-        'basic' AS summary_type, id, distribution,
-        generated_coverage, textual_holdings, show_generated
-    FROM serial.basic_summary
-    UNION
-    SELECT
-        'index' AS summary_type, id, distribution,
-        generated_coverage, textual_holdings, show_generated
-    FROM serial.index_summary
-    UNION
-    SELECT
-        'supplement' AS summary_type, id, distribution,
-        generated_coverage, textual_holdings, show_generated
-    FROM serial.supplement_summary ;
+            END IF;
 
+        END IF;
 
--- Given the IDs of two rows in actor.org_unit, *the second being an ancestor
--- of the first*, return in array form the path from the ancestor to the
--- descendant, with each point in the path being an org_unit ID.  This is
--- useful for sorting org_units by their position in a depth-first (display
--- order) representation of the tree.
---
--- This breaks with the precedent set by actor.org_unit_full_path() and others,
--- and gets the parameters "backwards," but otherwise this function would
--- not be very usable within json_query.
-CREATE OR REPLACE FUNCTION actor.org_unit_simple_path(INT, INT)
-RETURNS INT[] AS $$
-    WITH RECURSIVE descendant_depth(id, path) AS (
-        SELECT  aou.id,
-                ARRAY[aou.id]
-          FROM  actor.org_unit aou
-                JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
-          WHERE aou.id = $2
-            UNION ALL
-        SELECT  aou.id,
-                dd.path || ARRAY[aou.id]
-          FROM  actor.org_unit aou
-                JOIN actor.org_unit_type aout ON (aout.id = aou.ou_type)
-                JOIN descendant_depth dd ON (dd.id = aou.parent_ou)
-    ) SELECT dd.path
-        FROM actor.org_unit aou
-        JOIN descendant_depth dd USING (id)
-        WHERE aou.id = $1 ORDER BY dd.path;
-$$ LANGUAGE SQL STABLE;
+        visible_count := visible_count + 1;
 
-CREATE TABLE serial.materialized_holding_code (
-    id BIGSERIAL PRIMARY KEY,
-    issuance INTEGER NOT NULL REFERENCES serial.issuance (id) ON DELETE CASCADE,
-    subfield CHAR,
-    value TEXT
-);
+        current_res.id = core_result.id;
+        current_res.rel = core_result.rel;
 
-CREATE OR REPLACE FUNCTION serial.materialize_holding_code() RETURNS TRIGGER
-AS $func$ 
-use strict;
+        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;
 
-use MARC::Field;
-use JSON::XS;
+        IF tmp_int = 1 THEN
+            current_res.record = core_result.records[1];
+        ELSE
+            current_res.record = NULL;
+        END IF;
 
-if (not defined $_TD->{new}{holding_code}) {
-    elog(WARNING, 'NULL in "holding_code" column of serial.issuance allowed for now, but may not be useful');
-    return;
-}
+        RETURN NEXT current_res;
 
-# Do nothing if holding_code has not changed...
+        IF visible_count % 1000 = 0 THEN
+            -- RAISE NOTICE ' % visible so far ... ', visible_count;
+        END IF;
 
-if ($_TD->{new}{holding_code} eq $_TD->{old}{holding_code}) {
-    # ... unless the following internal flag is set.
+    END LOOP;
 
-    my $flag_rv = spi_exec_query(q{
-        SELECT * FROM config.internal_flag
-        WHERE name = 'serial.rematerialize_on_same_holding_code' AND enabled
-    }, 1);
-    return unless $flag_rv->{processed};
-}
+    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;
 
-my $holding_code = (new JSON::XS)->decode($_TD->{new}{holding_code});
+    RETURN NEXT current_res;
 
-my $field = new MARC::Field('999', @$holding_code); # tag doesnt matter
+END;
+$func$ LANGUAGE PLPGSQL;
 
-my $dstmt = spi_prepare(
-    'DELETE FROM serial.materialized_holding_code WHERE issuance = $1',
-    'INT'
+-- Drop the old 10-parameter function
+DROP FUNCTION IF EXISTS search.query_parser_fts (
+    INT, INT, TEXT, INT[], INT[], INT, INT, INT, BOOL, BOOL
 );
-spi_exec_prepared($dstmt, $_TD->{new}{id});
 
-my $istmt = spi_prepare(
-    q{
-        INSERT INTO serial.materialized_holding_code (
-            issuance, subfield, value
-        ) VALUES ($1, $2, $3)
-    }, qw{INT CHAR TEXT}
-);
+-- Evergreen DB patch 0705.data.custom-org-tree-perms.sql
+--
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0705', :eg_version);
 
-foreach ($field->subfields) {
-    spi_exec_prepared(
-        $istmt,
-        $_TD->{new}{id},
-        $_->[0],
-        $_->[1]
+INSERT INTO permission.perm_list (id, code, description)
+    VALUES (
+        528,
+        'ADMIN_ORG_UNIT_CUSTOM_TREE',
+        oils_i18n_gettext(
+            528,
+            'User may update custom org unit trees',
+            'ppl',
+            'description'
+        )
     );
-}
 
-return;
+-- Evergreen DB patch 0707.schema.acq-vandelay-integration.sql
 
-$func$ LANGUAGE 'plperlu';
+SELECT evergreen.upgrade_deps_block_check('0707', :eg_version);
 
-CREATE INDEX assist_holdings_display
-    ON serial.materialized_holding_code (issuance, subfield);
+-- seed data --
 
-CREATE TRIGGER materialize_holding_code
-    AFTER INSERT OR UPDATE ON serial.issuance
-    FOR EACH ROW EXECUTE PROCEDURE serial.materialize_holding_code() ;
+INSERT INTO permission.perm_list ( id, code, description )
+    VALUES (
+        529,
+        'ADMIN_IMPORT_MATCH_SET',
+        oils_i18n_gettext(
+            529,
+            'Allows a user to create/retrieve/update/delete vandelay match sets',
+            'ppl',
+            'description'
+        )
+    ), (
+        530,
+        'VIEW_IMPORT_MATCH_SET',
+        oils_i18n_gettext(
+            530,
+            'Allows a user to view vandelay match sets',
+            'ppl',
+            'description'
+        )
+    );
 
--- starting here, we materialize all existing holding codes.
+-- This upgrade script fixed a typo in a previous one. It was corrected in the proper place in this file.
+-- Still, record the fact it has been "applied".
+SELECT evergreen.upgrade_deps_block_check('0708', :eg_version);
 
-UPDATE config.internal_flag
-    SET enabled = TRUE
-    WHERE name = 'serial.rematerialize_on_same_holding_code';
+-- Evergreen DB patch 0709.data.misc_missing_perms.sql
 
-UPDATE serial.issuance SET holding_code = holding_code;
+SELECT evergreen.upgrade_deps_block_check('0709', :eg_version);
 
-UPDATE config.internal_flag
-    SET enabled = FALSE
-    WHERE name = 'serial.rematerialize_on_same_holding_code';
+INSERT INTO permission.perm_list ( id, code, description ) 
+    VALUES ( 
+        531, 
+        'ADMIN_ADDRESS_ALERT',
+        oils_i18n_gettext( 
+            531,
+            'Allows a user to create/retrieve/update/delete address alerts',
+            'ppl', 
+            'description' 
+        )
+    ), ( 
+        532, 
+        'VIEW_ADDRESS_ALERT',
+        oils_i18n_gettext( 
+            532,
+            'Allows a user to view address alerts',
+            'ppl', 
+            'description' 
+        )
+    ), ( 
+        533, 
+        'ADMIN_COPY_LOCATION_GROUP',
+        oils_i18n_gettext( 
+            533,
+            'Allows a user to create/retrieve/update/delete copy location groups',
+            'ppl', 
+            'description' 
+        )
+    ), ( 
+        534, 
+        'ADMIN_USER_ACTIVITY_TYPE',
+        oils_i18n_gettext( 
+            534,
+            'Allows a user to create/retrieve/update/delete user activity types',
+            'ppl', 
+            'description' 
+        )
+    );
 
--- finish holding code materialization process
+COMMIT;
 
--- fix up missing holding_code fields from serial.issuance
-UPDATE serial.issuance siss
-    SET holding_type = scap.type
-    FROM serial.caption_and_pattern scap
-    WHERE scap.id = siss.caption_and_pattern AND siss.holding_type IS NULL;
+\qecho ************************************************************************
+\qecho The following transaction, wrapping upgrade 0672, may take a while.  If
+\qecho it takes an unduly long time, try it outside of a transaction.
+\qecho ************************************************************************
 
+BEGIN;
 
--- Evergreen DB patch 0701.schema.patron_stat_category_enhancements.sql
+-- Evergreen DB patch 0672.fix-nonfiling-titles.sql
 --
--- Enables users to set patron statistical categories as required,
--- whether or not users can input free text for the category value.
--- Enables administrators to set an entry as the default for any
--- given patron statistical category and org unit.
+-- Titles that begin with non-filing articles using apostrophes
+-- (for example, "L'armée") get spaces injected between the article
+-- and the subsequent text, which then breaks searching for titles
+-- beginning with those articles.
+--
+-- This patch adds a nonfiling title element to MODS32 that can then
+-- be used to retrieve the title proper without affecting the spaces
+-- in the title. It's what we want, what we really really want, for
+-- title searches.
 --
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0701', :eg_version);
-
--- New table
-
-CREATE TABLE actor.stat_cat_entry_default (
-    id              SERIAL  PRIMARY KEY,
-    stat_cat_entry  INT     NOT NULL REFERENCES actor.stat_cat_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    stat_cat        INT     NOT NULL REFERENCES actor.stat_cat (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    owner           INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
-    CONSTRAINT sced_once_per_owner UNIQUE (stat_cat,owner)
-);
-
-COMMENT ON TABLE actor.stat_cat_entry_default IS $$
-User Statistical Category Default Entry
-
-A library may choose one of the stat_cat entries to be the
-default entry.
-$$;
-
--- Add columns to existing tables
-
--- Patron stat cat required column
-ALTER TABLE actor.stat_cat
-    ADD COLUMN required BOOL NOT NULL DEFAULT FALSE;
-
--- Patron stat cat allow_freetext column
-ALTER TABLE actor.stat_cat
-    ADD COLUMN allow_freetext BOOL NOT NULL DEFAULT TRUE;
-
--- Add permissions
 
-INSERT INTO permission.perm_list ( id, code, description ) VALUES
-    ( 525, 'CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 525, 
-        'User may set a default entry in a patron statistical category', 'ppl', 'description' )),
-    ( 526, 'UPDATE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 526, 
-        'User may reset a default entry in a patron statistical category', 'ppl', 'description' )),
-    ( 527, 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT', oils_i18n_gettext( 527, 
-        'User may unset a default entry in a patron statistical category', 'ppl', 'description' ));
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0672', :eg_version);
 
-INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
-    SELECT
-        pgt.id, perm.id, aout.depth, TRUE
-    FROM
-        permission.grp_tree pgt,
-        permission.perm_list perm,
-        actor.org_unit_type aout
-    WHERE
-        pgt.name = 'Circulation Administrator' AND
-        aout.name = 'System' AND
-        perm.code IN ('CREATE_PATRON_STAT_CAT_ENTRY_DEFAULT', 'DELETE_PATRON_STAT_CAT_ENTRY_DEFAULT');
+-- Update the XPath definition before the titleNonfiling element exists;
+-- but are you really going to read through the whole XSL below before
+-- seeing this important bit?
+UPDATE config.metabib_field
+    SET xpath = $$//mods32:mods/mods32:titleNonfiling[mods32:title and not (@type)]$$,
+        format = 'mods32'
+    WHERE field_class = 'title' AND name = 'proper';
 
+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
 
-SELECT evergreen.upgrade_deps_block_check('0702', :eg_version);
+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
 
-INSERT INTO config.global_flag (name, enabled, label) 
-    VALUES (
-        'opac.org_unit.non_inherited_visibility',
-        FALSE,
-        oils_i18n_gettext(
-            'opac.org_unit.non_inherited_visibility',
-            'Org Units Do Not Inherit Visibility',
-            'cgf',
-            'label'
-        )
-    );
+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
 
-CREATE TYPE actor.org_unit_custom_tree_purpose AS ENUM ('opac');
+Revision 1.9 subfield $y was added to field 242 2004/09/02 10:57 jrad
 
-CREATE TABLE actor.org_unit_custom_tree (
-    id              SERIAL  PRIMARY KEY,
-    active          BOOLEAN DEFAULT FALSE,
-    purpose         actor.org_unit_custom_tree_purpose NOT NULL DEFAULT 'opac' UNIQUE
-);
+Revision 1.8 Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
 
-CREATE TABLE actor.org_unit_custom_tree_node (
-    id              SERIAL  PRIMARY KEY,
-    tree            INTEGER REFERENCES actor.org_unit_custom_tree (id) DEFERRABLE INITIALLY DEFERRED,
-	org_unit        INTEGER NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
-	parent_node     INTEGER REFERENCES actor.org_unit_custom_tree_node (id) DEFERRABLE INITIALLY DEFERRED,
-    sibling_order   INTEGER NOT NULL DEFAULT 0,
-    CONSTRAINT aouctn_once_per_org UNIQUE (tree, org_unit)
-);
-    
+Revision 1.7 2004/03/25 08:29 jrad
 
-/* UNDO
-BEGIN;
-DELETE FROM config.global_flag WHERE name = 'opac.org_unit.non_inheritied_visibility';
-DROP TABLE actor.org_unit_custom_tree_node;
-DROP TABLE actor.org_unit_custom_tree;
-DROP TYPE actor.org_unit_custom_tree_purpose;
-COMMIT;
-*/
+Revision 1.6 various validation fixes 2004/02/20 ntra
 
-COMMIT;
+Revision 1.5  2003/10/02 16:18:58  ntra
+MODS2 to MODS3 updates, language unstacking and 
+de-duping, chopPunctuation expanded
 
--- This is split out because it was backported to 2.1, but may not exist before upgrades
--- It can safely fail
--- Also, lets say that. <_<
-\qecho
-\qecho *************************************************************************
-\qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-\qecho We are about to apply a patch that may not be needed. It can fail safely.
-\qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-\qecho *************************************************************************
-\qecho
+Revision 1.3  2003/04/03 00:07:19  ntra
+Revision 1.3 Additional Changes not related to MODS Version 2.0 by ntra
 
--- Evergreen DB patch 0693.schema.do_not_despace_issns.sql
---
--- FIXME: insert description of change, if needed
---
-BEGIN;
+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, at ind2)"/>
+						</nonSort>
+						<title>
+							<xsl:value-of select="substring($titleChop, at 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']">
+			<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: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:variable name="str">
+						<xsl:for-each select="marc:subfield">
+							<xsl:if test="(contains('adfklmor', at 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="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="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="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="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="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="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>
+		
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0693', :eg_version);
+		
+		
+		
+		<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">
+				<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=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>
+				<titleInfo>
+					<title>
+						<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>
+					</title>
+					<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">
+				<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: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>
 
--- FIXME: add/check SQL statements to perform the upgrade
--- Delete the index normalizer that was meant to remove spaces from ISSNs
--- but ended up breaking records with multiple ISSNs
-DELETE FROM config.metabib_field_index_norm_map WHERE id IN (
-    SELECT map.id FROM config.metabib_field_index_norm_map map
-        INNER JOIN config.metabib_field cmf ON cmf.id = map.field
-        INNER JOIN config.index_normalizer cin ON cin.id = map.norm
-    WHERE cin.func = 'replace'
-        AND cmf.field_class = 'identifier'
-        AND cmf.name = 'issn'
-        AND map.params = $$[" ",""]$$
-);
+				</url>
+			</location>
+		</xsl:for-each>
+			
+			<!-- 3.2 change tmee 856z  -->
 
--- Reindex records that have more than just a single ISSN
--- to ensure that spaces are maintained
-SELECT metabib.reingest_metabib_field_entries(source)
-  FROM metabib.identifier_field_entry mife
-    INNER JOIN config.metabib_field cmf ON cmf.id = mife.field
-  WHERE cmf.field_class = 'identifier'
-    AND cmf.name = 'issn'
-    AND char_length(value) > 9
-;
+		
+		<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: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, at code) and following-sibling::marc:subfield[@code=$axis])      or (contains($afterCodes, at 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="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: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">
+				<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>
+			<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>
+					<xsl:call-template name="part"></xsl:call-template>
+				</title>
+			</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="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="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: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="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>
 
-COMMIT;
+	<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>
 
--- Evergreen DB patch 0704.schema.query_parser_fts.sql
---
--- Add pref_ou query filter for preferred library searching
---
-BEGIN;
+	<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';
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0704', :eg_version);
+-- 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$
 
--- Create the new 11-parameter function, featuring param_pref_ou
-CREATE OR REPLACE FUNCTION search.query_parser_fts (
+    use strict;
+    use Unicode::Normalize;
+    use Encode;
 
-    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,
-    param_pref_ou   INT DEFAULT NULL
-) RETURNS SETOF search.search_result AS $func$
-DECLARE
+    my $str = decode_utf8(shift);
+    my $sf = shift;
 
-    current_res         search.search_result%ROWTYPE;
-    search_org_list     INT[];
-    luri_org_list       INT[];
-    tmp_int_list        INT[];
+    # 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.
 
-    check_limit         INT;
-    core_limit          INT;
-    core_offset         INT;
-    tmp_int             INT;
+    # 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;
 
-    core_result         RECORD;
-    core_cursor         REFCURSOR;
-    core_rel_query      TEXT;
+    # remove non-filing strings
+    $str =~ s/\x{0098}.*?\x{009C}//g;
 
-    total_count         INT := 0;
-    check_count         INT := 0;
-    deleted_count       INT := 0;
-    visible_count       INT := 0;
-    excluded_count      INT := 0;
+    $str = NFKD($str);
 
-BEGIN
+    # 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;
 
-    check_limit := COALESCE( param_check, 1000 );
-    core_limit  := COALESCE( param_limit, 25000 );
-    core_offset := COALESCE( param_offset, 0 );
+    # 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;
 
-    -- core_skip_chk := COALESCE( param_skip_chk, 1 );
+	if ($sf && $sf =~ /^a/o) {
+		my $commapos = index($str, ',');
+		if ($commapos > -1) {
+			if ($commapos != length($str) - 1) {
+                $str =~ s/,/\x07/; # preserve first comma
+			}
+		}
+	}
 
-    IF param_search_ou > 0 THEN
-        IF param_depth IS NOT NULL THEN
-            SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
-        ELSE
-            SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
-        END IF;
+    # 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}#,/;
 
-        SELECT array_accum(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou );
+    # 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/;
 
-    ELSIF param_search_ou < 0 THEN
-        SELECT array_accum(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
+    # intentionally skipping step 8 of the NACO algorithm; if the string
+    # gets normalized away, that's fine.
 
-        FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP
-            SELECT array_accum(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int );
-            luri_org_list := luri_org_list || tmp_int_list;
-        END LOOP;
+    # leading and trailing spaces
+    $str =~ s/\s+/ /g;
+    $str =~ s/^\s+//;
+    $str =~ s/\s+$//g;
 
-        SELECT array_accum(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id);
+    return lc $str;
+$func$ LANGUAGE 'plperlu' STRICT IMMUTABLE;
 
-    ELSIF param_search_ou = 0 THEN
-        -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
-    END IF;
+CREATE OR REPLACE FUNCTION public.search_normalize_keep_comma( TEXT ) RETURNS TEXT AS $func$
+        SELECT public.search_normalize($1,'a');
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
 
-    IF param_pref_ou IS NOT NULL THEN
-        SELECT array_accum(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;
+CREATE OR REPLACE FUNCTION public.search_normalize( TEXT ) RETURNS TEXT AS $func$
+	SELECT public.search_normalize($1,'');
+$func$ LANGUAGE 'sql' STRICT IMMUTABLE;
 
-    OPEN core_cursor FOR EXECUTE param_query;
+INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
+	'Search Normalize',
+	'Apply search normalization rules to the extracted text. A less extreme version of NACO normalization.',
+	'search_normalize',
+	0
+);
 
-    LOOP
+UPDATE config.metabib_field_index_norm_map
+    SET norm = (
+        SELECT id FROM config.index_normalizer WHERE func = 'search_normalize'
+    )
+    WHERE norm = (
+        SELECT id FROM config.index_normalizer WHERE func = 'naco_normalize'
+    )
+;
 
-        FETCH core_cursor INTO core_result;
-        EXIT WHEN NOT FOUND;
-        EXIT WHEN total_count >= core_limit;
 
-        total_count := total_count + 1;
+-- This could take a long time if you have a very non-English bib database
+-- Run it outside of a transaction to avoid lock escalation
+SELECT metabib.reingest_metabib_field_entries(record)
+    FROM metabib.full_rec
+    WHERE tag = '245'
+    AND subfield = 'a'
+    AND value LIKE '%''%'
+;
 
-        CONTINUE WHEN total_count NOT BETWEEN  core_offset + 1 AND check_limit + core_offset;
+COMMIT;
 
-        check_count := check_count + 1;
+-- This is split out because it takes forever to run on large bib collections.
+\qecho ************************************************************************
+\qecho The following transaction, wrapping upgrades 0679 and 0680, may take a
+\qecho *really* long time, and you might be able to run it by itself in
+\qecho parallel with other operations using a separate sesion.
+\qecho ************************************************************************
 
-        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;
+BEGIN;
+SELECT evergreen.upgrade_deps_block_check('0679', :eg_version);
 
-        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 ) );
+-- Address typo in column name
+ALTER TABLE config.metabib_class ADD COLUMN buoyant BOOL DEFAULT FALSE NOT NULL;
+UPDATE config.metabib_class SET buoyant = bouyant;
+ALTER TABLE config.metabib_class DROP COLUMN bouyant;
 
-        IF FOUND THEN
-            -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
-            visible_count := visible_count + 1;
+CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
+DECLARE
+    normalizer      RECORD;
+    value           TEXT := '';
+BEGIN
 
-            current_res.id = core_result.id;
-            current_res.rel = core_result.rel;
+    value := NEW.value;
 
-            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 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;
 
-            IF tmp_int = 1 THEN
-                current_res.record = core_result.records[1];
-            ELSE
-                current_res.record = NULL;
-            END IF;
+        END LOOP;
 
-            RETURN NEXT current_res;
+        NEW.value := value;
+    END IF;
 
-            CONTINUE;
-        END IF;
+    IF NEW.index_vector = ''::tsvector THEN
+        RETURN NEW;
+    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 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;
 
-        IF FOUND THEN
-            -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
-            visible_count := visible_count + 1;
+        END LOOP;
+    END IF;
 
-            current_res.id = core_result.id;
-            current_res.rel = core_result.rel;
+    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);
+    END IF;
 
-            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;
+    NEW.index_vector = to_tsvector((TG_ARGV[0])::regconfig, value);
 
-            IF tmp_int = 1 THEN
-                current_res.record = core_result.records[1];
-            ELSE
-                current_res.record = NULL;
-            END IF;
+    RETURN NEW;
+END;
+$$ LANGUAGE PLPGSQL;
 
-            RETURN NEXT current_res;
+-- Given a string such as a user might type into a search box, prepare
+-- two changed variants for TO_TSQUERY(). See
+-- http://www.postgresql.org/docs/9.0/static/textsearch-controls.html
+-- The first variant is normalized to match indexed documents regardless
+-- of diacritics.  The second variant keeps its diacritics for proper
+-- highlighting via TS_HEADLINE().
+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$';
 
-            CONTINUE;
-        END IF;
+    orig := ARRAY_TO_STRING(
+        evergreen.regexp_split_to_array(orig, E'\\W+'), ' '
+    );
 
-        IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
+    normalized := public.search_normalize(orig); -- also trim()s
+    plain := trim(orig);
 
-            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 orig_ended_in_space THEN
+        plain := plain || ':*';
+        normalized := normalized || ':*';
+    END IF;
 
-            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;
+    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;
 
-                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;
+-- Definition of OUT parameters changes, so must drop first
+DROP FUNCTION IF EXISTS metabib.suggest_browse_entries (TEXT, TEXT, TEXT, INTEGER, INTEGER, INTEGER);
 
-        IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
+CREATE OR REPLACE
+    FUNCTION metabib.suggest_browse_entries(
+        raw_query_text  TEXT,   -- actually typed by humans at the UI level
+        search_class    TEXT,   -- 'alias' or 'class' or 'class|field..', etc
+        headline_opts   TEXT,   -- markup options for ts_headline()
+        visibility_org  INTEGER,-- null if you don't want opac visibility test
+        query_limit     INTEGER,-- use in LIMIT clause of interal query
+        normalization   INTEGER -- argument to TS_RANK_CD()
+    ) RETURNS TABLE (
+        value                   TEXT,   -- plain
+        field                   INTEGER,
+        buoyant_and_class_match BOOL,
+        field_match             BOOL,
+        field_weight            INTEGER,
+        rank                    REAL,
+        buoyant                 BOOL,
+        match                   TEXT    -- marked up
+    ) AS $func$
+DECLARE
+    prepared_query_texts    TEXT[];
+    query                   TSQUERY;
+    plain_query             TSQUERY;
+    opac_visibility_join    TEXT;
+    search_class_join       TEXT;
+    r_fields                RECORD;
+BEGIN
+    prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
 
-            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;
+    query := TO_TSQUERY('keyword', prepared_query_texts[1]);
+    plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
 
-            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 visibility_org IS NOT NULL THEN
+        opac_visibility_join := '
+    JOIN asset.opac_visible_copies aovc ON (
+        aovc.record = mbedm.source AND
+        aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
+    )';
+    ELSE
+        opac_visibility_join := '';
+    END IF;
 
-                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;
+    -- The following determines whether we only provide suggestsons matching
+    -- the user's selected search_class, or whether we show other suggestions
+    -- too. The reason for MIN() is that for search_classes like
+    -- 'title|proper|uniform' you would otherwise get multiple rows.  The
+    -- implication is that if title as a class doesn't have restrict,
+    -- nor does the proper field, but the uniform field does, you're going
+    -- to get 'false' for your overall evaluation of 'should we restrict?'
+    -- To invert that, change from MIN() to MAX().
 
-        END IF;
+    SELECT
+        INTO r_fields
+            MIN(cmc.restrict::INT) AS restrict_class,
+            MIN(cmf.restrict::INT) AS restrict_field
+        FROM metabib.search_class_to_registered_components(search_class)
+            AS _registered (field_class TEXT, field INT)
+        JOIN
+            config.metabib_class cmc ON (cmc.name = _registered.field_class)
+        LEFT JOIN
+            config.metabib_field cmf ON (cmf.id = _registered.field);
 
-        IF staff IS NULL OR NOT staff THEN
+    -- evaluate 'should we restrict?'
+    IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
+        search_class_join := '
+    JOIN
+        metabib.search_class_to_registered_components($2)
+        AS _registered (field_class TEXT, field INT) ON (
+            (_registered.field IS NULL AND
+                _registered.field_class = cmf.field_class) OR
+            (_registered.field = cmf.id)
+        )
+    ';
+    ELSE
+        search_class_join := '
+    LEFT JOIN
+        metabib.search_class_to_registered_components($2)
+        AS _registered (field_class TEXT, field INT) ON (
+            _registered.field_class = cmc.name
+        )
+    ';
+    END IF;
 
-            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;
+    RETURN QUERY EXECUTE 'SELECT *, TS_HEADLINE(value, $7, $3) FROM (SELECT DISTINCT
+        mbe.value,
+        cmf.id,
+        cmc.buoyant AND _registered.field_class IS NOT NULL,
+        _registered.field = cmf.id,
+        cmf.weight,
+        TS_RANK_CD(mbe.index_vector, $1, $6),
+        cmc.buoyant
+    FROM metabib.browse_entry_def_map mbedm
+    JOIN metabib.browse_entry mbe ON (mbe.id = mbedm.entry)
+    JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
+    JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
+    '  || search_class_join || opac_visibility_join ||
+    ' WHERE $1 @@ mbe.index_vector
+    ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
+    LIMIT $5) x
+    ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
+    '   -- sic, repeat the order by clause in the outer select too
+    USING
+        query, search_class, headline_opts,
+        visibility_org, query_limit, normalization, plain_query
+        ;
 
-            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;
+    -- sort order:
+    --  buoyant AND chosen class = match class
+    --  chosen field = match field
+    --  field weight
+    --  rank
+    --  buoyancy
+    --  value itself
 
-                IF NOT FOUND THEN
+END;
+$func$ LANGUAGE PLPGSQL;
 
-                    -- 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
+\qecho 
+\qecho The following takes about a minute per 100,000 rows in
+\qecho metabib.browse_entry on my development system, which is only a VM with
+\qecho 4 GB of memory and 2 cores.
+\qecho 
+\qecho The following is a very loose estimate of how long the next UPDATE
+\qecho statement would take to finish on MY machine, based on YOUR number
+\qecho of rows in metabib.browse_entry:
+\qecho 
 
-            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;
+SELECT (COUNT(id) / 100000.0) * INTERVAL '1 minute'
+    AS "approximate duration of following UPDATE statement"
+    FROM metabib.browse_entry;
 
-            IF NOT FOUND THEN
+UPDATE metabib.browse_entry SET index_vector = TO_TSVECTOR(
+    'keyword',
+    public.search_normalize(
+        ARRAY_TO_STRING(
+            evergreen.regexp_split_to_array(value, E'\\W+'), ' '
+        )
+    )
+);
 
-                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
+SELECT evergreen.upgrade_deps_block_check('0680', :eg_version);
 
-                    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;
+-- Not much use in having identifier-class fields be suggestions. Credit for the idea goes to Ben Shum.
+UPDATE config.metabib_field SET browse_field = FALSE WHERE id < 100 AND field_class = 'identifier';
 
-                    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;
+---------------------------------------------------------------------------
+-- The rest of this was tested on Evergreen Indiana's dev server, which has
+-- a large data set  of 2.6M bibs, and was instrumental in sussing out the
+-- needed adjustments.  Thanks, EG-IN!
+---------------------------------------------------------------------------
 
-        END IF;
+-- GIN indexes are /much/ better for prefix matching, which is important for browse and autosuggest
+--Commented out the creation earlier, so we don't need to drop it here.
+--DROP INDEX metabib.metabib_browse_entry_index_vector_idx;
+CREATE INDEX metabib_browse_entry_index_vector_idx ON metabib.browse_entry USING GIN (index_vector);
 
-        visible_count := visible_count + 1;
 
-        current_res.id = core_result.id;
-        current_res.rel = core_result.rel;
+-- We need thes to make the autosuggest limiting joins fast
+CREATE INDEX browse_entry_def_map_def_idx ON metabib.browse_entry_def_map (def);
+CREATE INDEX browse_entry_def_map_entry_idx ON metabib.browse_entry_def_map (entry);
+CREATE INDEX browse_entry_def_map_source_idx ON metabib.browse_entry_def_map (source);
+
+-- In practice this will always be ~1 row, and the default of 1000 causes terrible plans
+ALTER FUNCTION metabib.search_class_to_registered_components(text) ROWS 1;
+
+-- Reworking of the generated query to act in a sane manner in the face of large datasets
+CREATE OR REPLACE
+    FUNCTION metabib.suggest_browse_entries(
+        raw_query_text  TEXT,   -- actually typed by humans at the UI level
+        search_class    TEXT,   -- 'alias' or 'class' or 'class|field..', etc
+        headline_opts   TEXT,   -- markup options for ts_headline()
+        visibility_org  INTEGER,-- null if you don't want opac visibility test
+        query_limit     INTEGER,-- use in LIMIT clause of interal query
+        normalization   INTEGER -- argument to TS_RANK_CD()
+    ) RETURNS TABLE (
+        value                   TEXT,   -- plain
+        field                   INTEGER,
+        buoyant_and_class_match BOOL,
+        field_match             BOOL,
+        field_weight            INTEGER,
+        rank                    REAL,
+        buoyant                 BOOL,
+        match                   TEXT    -- marked up
+    ) AS $func$
+DECLARE
+    prepared_query_texts    TEXT[];
+    query                   TSQUERY;
+    plain_query             TSQUERY;
+    opac_visibility_join    TEXT;
+    search_class_join       TEXT;
+    r_fields                RECORD;
+BEGIN
+    prepared_query_texts := metabib.autosuggest_prepare_tsquery(raw_query_text);
+
+    query := TO_TSQUERY('keyword', prepared_query_texts[1]);
+    plain_query := TO_TSQUERY('keyword', prepared_query_texts[2]);
 
-        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 visibility_org IS NOT NULL THEN
+        opac_visibility_join := '
+    JOIN asset.opac_visible_copies aovc ON (
+        aovc.record = x.source AND
+        aovc.circ_lib IN (SELECT id FROM actor.org_unit_descendants($4))
+    )';
+    ELSE
+        opac_visibility_join := '';
+    END IF;
 
-        IF tmp_int = 1 THEN
-            current_res.record = core_result.records[1];
-        ELSE
-            current_res.record = NULL;
-        END IF;
+    -- The following determines whether we only provide suggestsons matching
+    -- the user's selected search_class, or whether we show other suggestions
+    -- too. The reason for MIN() is that for search_classes like
+    -- 'title|proper|uniform' you would otherwise get multiple rows.  The
+    -- implication is that if title as a class doesn't have restrict,
+    -- nor does the proper field, but the uniform field does, you're going
+    -- to get 'false' for your overall evaluation of 'should we restrict?'
+    -- To invert that, change from MIN() to MAX().
 
-        RETURN NEXT current_res;
+    SELECT
+        INTO r_fields
+            MIN(cmc.restrict::INT) AS restrict_class,
+            MIN(cmf.restrict::INT) AS restrict_field
+        FROM metabib.search_class_to_registered_components(search_class)
+            AS _registered (field_class TEXT, field INT)
+        JOIN
+            config.metabib_class cmc ON (cmc.name = _registered.field_class)
+        LEFT JOIN
+            config.metabib_field cmf ON (cmf.id = _registered.field);
 
-        IF visible_count % 1000 = 0 THEN
-            -- RAISE NOTICE ' % visible so far ... ', visible_count;
-        END IF;
+    -- evaluate 'should we restrict?'
+    IF r_fields.restrict_field::BOOL OR r_fields.restrict_class::BOOL THEN
+        search_class_join := '
+    JOIN
+        metabib.search_class_to_registered_components($2)
+        AS _registered (field_class TEXT, field INT) ON (
+            (_registered.field IS NULL AND
+                _registered.field_class = cmf.field_class) OR
+            (_registered.field = cmf.id)
+        )
+    ';
+    ELSE
+        search_class_join := '
+    LEFT JOIN
+        metabib.search_class_to_registered_components($2)
+        AS _registered (field_class TEXT, field INT) ON (
+            _registered.field_class = cmc.name
+        )
+    ';
+    END IF;
 
-    END LOOP;
+    RETURN QUERY EXECUTE '
+SELECT  DISTINCT
+        x.value,
+        x.id,
+        x.push,
+        x.restrict,
+        x.weight,
+        x.ts_rank_cd,
+        x.buoyant,
+        TS_HEADLINE(value, $7, $3)
+  FROM  (SELECT DISTINCT
+                mbe.value,
+                cmf.id,
+                cmc.buoyant AND _registered.field_class IS NOT NULL AS push,
+                _registered.field = cmf.id AS restrict,
+                cmf.weight,
+                TS_RANK_CD(mbe.index_vector, $1, $6),
+                cmc.buoyant,
+                mbedm.source
+          FROM  metabib.browse_entry_def_map mbedm
 
-    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;
+                -- Start with a pre-limited set of 10k possible suggestions. More than that is not going to be useful anyway
+                JOIN (SELECT * FROM metabib.browse_entry WHERE index_vector @@ $1 LIMIT 10000) mbe ON (mbe.id = mbedm.entry)
 
-    CLOSE core_cursor;
+                JOIN config.metabib_field cmf ON (cmf.id = mbedm.def)
+                JOIN config.metabib_class cmc ON (cmf.field_class = cmc.name)
+                '  || search_class_join || '
+          ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
+          LIMIT 1000) AS x -- This outer limit makes testing for opac visibility usably fast
+        ' || opac_visibility_join || '
+  ORDER BY 3 DESC, 4 DESC NULLS LAST, 5 DESC, 6 DESC, 7 DESC, 1 ASC
+  LIMIT $5
+'   -- sic, repeat the order by clause in the outer select too
+    USING
+        query, search_class, headline_opts,
+        visibility_org, query_limit, normalization, plain_query
+        ;
 
-    RETURN NEXT current_res;
+    -- sort order:
+    --  buoyant AND chosen class = match class
+    --  chosen field = match field
+    --  field weight
+    --  rank
+    --  buoyancy
+    --  value itself
 
 END;
 $func$ LANGUAGE PLPGSQL;
 
--- Drop the old 10-parameter function
-DROP FUNCTION IF EXISTS search.query_parser_fts (
-    INT, INT, TEXT, INT[], INT[], INT, INT, INT, BOOL, BOOL
-);
-
 COMMIT;
 
--- Evergreen DB patch 0705.data.custom-org-tree-perms.sql
+-- This is split out because it was backported to 2.1, but may not exist before upgrades
+-- It can safely fail
+-- Also, lets say that. <_<
+\qecho
+\qecho *************************************************************************
+\qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+\qecho We are about to apply a patch that may not be needed. It can fail safely.
+\qecho !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+\qecho *************************************************************************
+\qecho
+
+-- Evergreen DB patch 0693.schema.do_not_despace_issns.sql
+--
+-- FIXME: insert description of change, if needed
 --
 BEGIN;
 
--- check whether patch can be applied
-SELECT evergreen.upgrade_deps_block_check('0705', :eg_version);
-
-INSERT INTO permission.perm_list (id, code, description)
-    VALUES (
-        528,
-        'ADMIN_ORG_UNIT_CUSTOM_TREE',
-        oils_i18n_gettext(
-            528,
-            'User may update custom org unit trees',
-            'ppl',
-            'description'
-        )
-    );
-
-COMMIT;
-
 
--- Evergreen DB patch 0707.schema.acq-vandelay-integration.sql
-BEGIN;
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0693', :eg_version);
 
-SELECT evergreen.upgrade_deps_block_check('0707', :eg_version);
+-- FIXME: add/check SQL statements to perform the upgrade
+-- Delete the index normalizer that was meant to remove spaces from ISSNs
+-- but ended up breaking records with multiple ISSNs
+DELETE FROM config.metabib_field_index_norm_map WHERE id IN (
+    SELECT map.id FROM config.metabib_field_index_norm_map map
+        INNER JOIN config.metabib_field cmf ON cmf.id = map.field
+        INNER JOIN config.index_normalizer cin ON cin.id = map.norm
+    WHERE cin.func = 'replace'
+        AND cmf.field_class = 'identifier'
+        AND cmf.name = 'issn'
+        AND map.params = $$[" ",""]$$
+);
 
--- seed data --
+-- Reindex records that have more than just a single ISSN
+-- to ensure that spaces are maintained
+SELECT metabib.reingest_metabib_field_entries(source)
+  FROM metabib.identifier_field_entry mife
+    INNER JOIN config.metabib_field cmf ON cmf.id = mife.field
+  WHERE cmf.field_class = 'identifier'
+    AND cmf.name = 'issn'
+    AND char_length(value) > 9
+;
 
-INSERT INTO permission.perm_list ( id, code, description )
-    VALUES (
-        529,
-        'ADMIN_IMPORT_MATCH_SET',
-        oils_i18n_gettext(
-            529,
-            'Allows a user to create/retrieve/update/delete vandelay match sets',
-            'ppl',
-            'description'
-        )
-    ), (
-        530,
-        'VIEW_IMPORT_MATCH_SET',
-        oils_i18n_gettext(
-            530,
-            'Allows a user to view vandelay match sets',
-            'ppl',
-            'description'
-        )
-    );
 
 COMMIT;
 
--- Evergreen DB patch 0709.data.misc_missing_perms.sql
---
--- Fixes a typo in the name of a global flag
-
-BEGIN;
-
-SELECT evergreen.upgrade_deps_block_check('0709', :eg_version);
-
-INSERT INTO permission.perm_list ( id, code, description ) 
-    VALUES ( 
-        531, 
-        'ADMIN_ADDRESS_ALERT',
-        oils_i18n_gettext( 
-            531,
-            'Allows a user to create/retrieve/update/delete address alerts',
-            'ppl', 
-            'description' 
-        )
-    ), ( 
-        532, 
-        'VIEW_ADDRESS_ALERT',
-        oils_i18n_gettext( 
-            532,
-            'Allows a user to view address alerts',
-            'ppl', 
-            'description' 
-        )
-    ), ( 
-        533, 
-        'ADMIN_COPY_LOCATION_GROUP',
-        oils_i18n_gettext( 
-            533,
-            'Allows a user to create/retrieve/update/delete copy location groups',
-            'ppl', 
-            'description' 
-        )
-    ), ( 
-        534, 
-        'ADMIN_USER_ACTIVITY_TYPE',
-        oils_i18n_gettext( 
-            534,
-            'Allows a user to create/retrieve/update/delete user activity types',
-            'ppl', 
-            'description' 
-        )
-    );
-
-COMMIT;

-----------------------------------------------------------------------

Summary of changes:
 .../sql/Pg/version-upgrade/2.1-2.2-upgrade-db.sql  |12698 ++++++++++----------
 1 files changed, 6352 insertions(+), 6346 deletions(-)


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list