Merge branch 'master' of git.evergreen-ils.org:Evergreen into template-toolkit-opac
authorMike Rylander <mrylander@gmail.com>
Mon, 11 Jul 2011 20:16:21 +0000 (16:16 -0400)
committerMike Rylander <mrylander@gmail.com>
Mon, 11 Jul 2011 20:16:21 +0000 (16:16 -0400)
60 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/c-apps/oils_sql.c
Open-ILS/src/javascript/backend/catalog/fixed_fields.js
Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/Authority.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Authority.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/biblio.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/dbi.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/SuperCat.pm
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/011.schema.authority.sql
Open-ILS/src/sql/Pg/012.schema.vandelay.sql
Open-ILS/src/sql/Pg/020.schema.functions.sql
Open-ILS/src/sql/Pg/030.schema.metabib.sql
Open-ILS/src/sql/Pg/300.schema.staged_search.sql
Open-ILS/src/sql/Pg/800.fkeys.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/999.functions.global.sql
Open-ILS/src/sql/Pg/upgrade/0573.schema.staff_search_find_no_copies.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0574.data.hold_pull_list_template.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0575.schema.authority-control-sets.sql [new file with mode: 0644]
Open-ILS/web/Makefile.am
Open-ILS/web/js/dojo/MARC/FixedFields.js [new file with mode: 0644]
Open-ILS/web/js/dojo/MARC/Record.js
Open-ILS/web/js/dojo/openils/AuthorityControlSet.js [new file with mode: 0644]
Open-ILS/web/js/dojo/openils/authority/nls/authority.js
Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
Open-ILS/web/js/ui/default/acq/common/li_table.js
Open-ILS/web/js/ui/default/cat/authority/list.js
Open-ILS/web/js/ui/default/conify/global/cat/authority/common.js [new file with mode: 0644]
Open-ILS/web/opac/common/js/config.js
Open-ILS/web/opac/extras/circ/alt_holds_print.html
Open-ILS/web/opac/extras/circ/alt_holds_print.js
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/web/opac/locale/en-US/opac.dtd
Open-ILS/web/opac/skin/default/css/layout.css
Open-ILS/web/opac/skin/default/js/advanced.js
Open-ILS/web/opac/skin/default/js/authbrowse.js [new file with mode: 0644]
Open-ILS/web/opac/skin/default/js/holds.js
Open-ILS/web/opac/skin/default/xml/common/holds.xml
Open-ILS/web/opac/skin/default/xml/common/js_common.xml
Open-ILS/web/opac/skin/default/xml/common/sidebar.xml
Open-ILS/web/opac/skin/default/xml/page_authbrowse.xml [new file with mode: 0644]
Open-ILS/web/opac/skin/default/xml/setenv.xml
Open-ILS/web/templates/default/cat/authority/list.tt2
Open-ILS/web/templates/default/conify/global/cat/authority/browse_axis.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/conify/global/cat/authority/browse_axis_authority_field_map.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/conify/global/cat/authority/control_set.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/conify/global/cat/authority/control_set_authority_field.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/conify/global/cat/authority/control_set_bib_field.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/conify/global/cat/authority/thesaurus.tt2 [new file with mode: 0644]
Open-ILS/xul/staff_client/chrome/content/main/menu.js
Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties
Open-ILS/xul/staff_client/server/cat/copy_editor.js
Open-ILS/xul/staff_client/server/cat/marcedit.js

index 3a138a4..9b224df 100644 (file)
@@ -1757,6 +1757,144 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <link field="item" reltype="has_a" key="id" map="" class="ccbi"/>
                </links>
        </class>
+
+       <class id="acs" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="authority::control_set" oils_persist:tablename="authority.control_set" reporter:label="Authority Control Set" oils_persist:field_safe="true">
+               <fields oils_persist:primary="id" oils_persist:sequence="authority.control_set_id_seq">
+                       <field reporter:label="Control Set ID" name="id" reporter:datatype="id" reporter:selector="name"/>
+                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true" oils_obj:required="true" />
+                       <field reporter:label="Description" name="description" reporter:datatype="text" oils_persist:i18n="true" />
+                       <field reporter:label="Controlling Authority Fields" name="authority_fields" reporter:datatype="link" oils_persist:virtual="true"/>
+                       <field reporter:label="Thesauri" name="thesauri" reporter:datatype="link" oils_persist:virtual="true"/>
+               </fields>
+               <links>
+                       <link field="authority_fields" reltype="has_many" key="control_set" map="" class="acsaf"/>
+                       <link field="thesauri" reltype="has_many" key="control_set" map="" class="at"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="CREATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <retrieve/>
+                               <update permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <delete permission="DELETE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
+       <class id="acsaf" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="authority::control_set_authority_field" oils_persist:tablename="authority.control_set_authority_field" reporter:label="Authority Control Set Authority Field" oils_persist:field_safe="true">
+               <fields oils_persist:primary="id" oils_persist:sequence="authority.control_set_authority_field_id_seq">
+                       <field reporter:label="Control Set Authority Field ID" name="id" reporter:datatype="id" reporter:selector="name"/>
+                       <field reporter:label="Main Entry" name="main_entry" reporter:datatype="link"/>
+                       <field reporter:label="Control Set" name="control_set" reporter:datatype="link"/>
+                       <field reporter:label="Tag" name="tag" reporter:datatype="text" oils_obj:required="true" oils_obj:validate="^.{3}$"/>
+                       <field reporter:label="Subfield List" name="sf_list" reporter:datatype="text" />
+                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true" oils_obj:required="true" />
+                       <field reporter:label="Description" name="description" reporter:datatype="text" oils_persist:i18n="true" />
+                       <field reporter:label="Subordinate Entries" name="sub_entries" reporter:datatype="link" oils_persist:virtual="true"/>
+                       <field reporter:label="Controlled Bib Fields" name="bib_fields" reporter:datatype="link" oils_persist:virtual="true"/>
+                       <field reporter:label="Thesauri" name="thesauri" reporter:datatype="link" oils_persist:virtual="true"/>
+                       <field reporter:label="Browse Axis Maps" name="axis_maps" reporter:datatype="link" oils_persist:virtual="true"/>
+               </fields>
+               <links>
+                       <link field="axis_maps" reltype="has_many" key="field" map="" class="abaafm"/>
+                       <link field="control_set" reltype="has_a" key="id" map="" class="acs"/>
+                       <link field="bib_fields" reltype="has_many" key="authority_field" map="" class="acsbf"/>
+                       <link field="thesauri" reltype="has_many" key="control_set" map="" class="at"/>
+                       <link field="main_entry" reltype="has_a" key="id" map="" class="acsaf"/>
+                       <link field="sub_entries" reltype="has_many" key="main_entry" map="" class="acsaf"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="CREATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <retrieve/>
+                               <update permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <delete permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
+       <class id="acsbf" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="authority::control_set_bib_field" oils_persist:tablename="authority.control_set_bib_field" reporter:label="Authority Control Set Bib Field" oils_persist:field_safe="true">
+               <fields oils_persist:primary="id" oils_persist:sequence="authority.control_set_bib_field_id_seq">
+                       <field reporter:label="Controlled Bib Field ID" name="id" reporter:datatype="id" reporter:selector="name"/>
+                       <field reporter:label="Controlling Authority Field" name="authority_field" reporter:datatype="link"/>
+                       <field reporter:label="Tag" name="tag" reporter:datatype="text" oils_obj:required="true" oils_obj:validate="^.{3}$"/>
+               </fields>
+               <links>
+                       <link field="authority_field" reltype="has_a" key="id" map="" class="acsaf"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="CREATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <retrieve/>
+                               <update permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <delete permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
+       <class id="at" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="authority::thesaurus" oils_persist:tablename="authority.thesaurus" reporter:label="Authority Thesaurus" oils_persist:field_safe="true">
+               <fields oils_persist:primary="code">
+                       <field reporter:label="Thesaurus Code" name="code" reporter:datatype="id" reporter:selector="name"/>
+                       <field reporter:label="Control Set" name="control_set" reporter:datatype="link"/>
+                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true" oils_obj:required="true" />
+                       <field reporter:label="Description" name="description" reporter:datatype="text" oils_persist:i18n="true" />
+               </fields>
+               <links>
+                       <link field="control_set" reltype="has_a" key="id" map="" class="acs"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="CREATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <retrieve/>
+                               <update permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <delete permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
+       <class id="aba" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="authority::browse_axis" oils_persist:tablename="authority.browse_axis" reporter:label="Authority Browse Axis" oils_persist:field_safe="true">
+               <fields oils_persist:primary="code">
+                       <field reporter:label="Code" name="code" reporter:datatype="id" reporter:selector="name" oils_obj:validate="^\S+$"/>
+                       <field reporter:label="Name" name="name" reporter:datatype="text" oils_persist:i18n="true" oils_obj:required="true"/>
+                       <field reporter:label="Sorter Attribute" name="sorter" reporter:datatype="link"/>
+                       <field reporter:label="Description" name="description" reporter:datatype="text"/>
+                       <field reporter:label="Authority Fields" name="fields" reporter:datatype="link" oils_persist:virtual="true"/>
+                       <field reporter:label="Authority Field Maps" name="maps" reporter:datatype="link" oils_persist:virtual="true"/>
+               </fields>
+               <links>
+                       <link field="sorter" reltype="has_a" key="name" map="" class="crad"/>
+                       <link field="fields" reltype="has_many" key="axis" map="field" class="abaafm"/>
+                       <link field="maps" reltype="has_many" key="axis" map="" class="abaafm"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <create permission="CREATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <retrieve/>
+                               <update permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                               <delete permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                       </actions>
+               </permacrud>
+       </class>
+
+    <class id="abaafm" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="authority::browse_axis_authority_field_map" oils_persist:tablename="authority.browse_axis_authority_field_map" reporter:label="Authority Browse Axis Field Map" oils_persist:field_safe="true">
+        <fields oils_persist:primary="id" oils_persist:sequence="authority.browse_axis_authority_field_map_id_seq">
+            <field reporter:label="Axis Authority Field Map ID" name="id" reporter:datatype="id"/>
+            <field reporter:label="Authority Field" name="field" oils_obj:required="true" reporter:datatype="link"/>
+            <field reporter:label="Axis" name="axis" reporter:datatype="link" oils_obj:required="true"/>
+        </fields>
+        <links>
+            <link field="field" reltype="has_a" key="id" map="" class="acsaf"/>
+            <link field="axis" reltype="has_a" key="code" map="" class="aba"/>
+        </links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="CREATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                <retrieve/>
+                <update permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+                <delete permission="UPDATE_AUTHORITY_CONTROL_SET" global_required="true"/>
+            </actions>
+        </permacrud>
+    </class>
+
        <class id="are" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="authority::record_entry" oils_persist:tablename="authority.record_entry" reporter:label="Authority Record Entry">
                <fields oils_persist:primary="id" oils_persist:sequence="authority.record_entry_id_seq">
                        <field name="active" reporter:datatype="bool"/>
@@ -1769,12 +1907,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field name="last_xact_id" />
                        <field name="marc" />
                        <field name="source" />
+                       <field reporter:label="Control Set" name="control_set" reporter:datatype="link"/>
                        <field reporter:label="Owner" name="owner"  reporter:datatype="org_unit"/>
                        <field name="fixed_fields" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field name="notes" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field name="bib_links" oils_persist:virtual="true" reporter:datatype="link"/>
                </fields>
                <links>
+                       <link field="control_set" reltype="has_a" key="id" map="" class="acs"/>
                        <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
                        <link field="editor" reltype="has_a" key="id" map="" class="au"/>
                        <link field="creator" reltype="has_a" key="id" map="" class="au"/>
@@ -1797,9 +1937,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field name="id" />
                        <field name="record" />
                        <field name="record_status" />
+                       <field name="thesaurus" />
                </fields>
                <links>
                        <link field="record" reltype="has_a" key="id" map="" class="are"/>
+                       <link field="thesaurus" reltype="might_have" key="code" map="" class="at"/>
                </links>
        </class>
        <class id="abl" controller="open-ils.cstore" oils_obj:fieldmapper="authority::bib_linking" oils_persist:tablename="authority.bib_linking" reporter:label="Authority-Bibliographic Record Link">
index 3f8157d..b79c579 100644 (file)
@@ -2765,16 +2765,25 @@ static char* searchFieldTransformPredicate( const ClassInfo* class_info, osrfHas
                right_parens = ")";
        }
 
+       const char* right_percent = "";
+       const char* real_op       = op;
+
+       if( !strcasecmp( op, "startwith") ) {
+               real_op = "like";
+               right_percent = "|| '%'";
+       }
+
        growing_buffer* sql_buf = buffer_init( 32 );
 
        buffer_fadd(
                sql_buf,
-               "%s%s %s %s %s %s%s",
+               "%s%s %s %s %s%s %s%s",
                left_parens,
                field_transform,
-               op,
+               real_op,
                left_parens,
                value,
+               right_percent,
                right_parens,
                right_parens
        );
index 4e7871c..6a67b65 100644 (file)
 
 var rec_type = {
-        BKS : { Type : /[at]{1}/,      BLvl : /[acdm]{1}/ },
-       SER : { Type : /[a]{1}/,        BLvl : /[bsi]{1}/ },
-       VIS : { Type : /[gkro]{1}/,     BLvl : /[abcdmsi]{1}/ },
-       MIX : { Type : /[p]{1}/,        BLvl : /[cdi]{1}/ },
-       MAP : { Type : /[ef]{1}/,       BLvl : /[abcdmsi]{1}/ },
-       SCO : { Type : /[cd]{1}/,       BLvl : /[abcdmsi]{1}/ },
-       REC : { Type : /[ij]{1}/,       BLvl : /[abcdmsi]{1}/ },
-       COM : { Type : /[m]{1}/,        BLvl : /[abcdmsi]{1}/ }
+    BKS : { Type : /[at]{1}/,    BLvl : /[acdm]{1}/ },
+    SER : { Type : /[a]{1}/,    BLvl : /[bsi]{1}/ },
+    VIS : { Type : /[gkro]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    MIX : { Type : /[p]{1}/,    BLvl : /[cdi]{1}/ },
+    MAP : { Type : /[ef]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    SCO : { Type : /[cd]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    REC : { Type : /[ij]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    COM : { Type : /[m]{1}/,    BLvl : /[abcdmsi]{1}/ },
+    AUT : { Type : /[z]{1}/,    BLvl : /.{1}/ },
+    MFHD : { Type : /[uvxy]{1}/,  BLvl : /.{1}/ }
 };
 
 var ff_pos = {
-       Ctry : {
-               _8 : {
-                       BKS : {start : 15, len : 3, def : ' ' },
-                       SER : {start : 15, len : 3, def : ' ' },
-                       VIS : {start : 15, len : 3, def : ' ' },
-                       MIX : {start : 15, len : 3, def : ' ' },
-                       MAP : {start : 15, len : 3, def : ' ' },
-                       SCO : {start : 15, len : 3, def : ' ' },
-                       REC : {start : 15, len : 3, def : ' ' },
-                       COM : {start : 15, len : 3, def : ' ' },
-               }
-       },
-       Lang : {
-               _8 : {
-                       BKS : {start : 35, len : 3, def : ' ' },
-                       SER : {start : 35, len : 3, def : ' ' },
-                       VIS : {start : 35, len : 3, def : ' ' },
-                       MIX : {start : 35, len : 3, def : ' ' },
-                       MAP : {start : 35, len : 3, def : ' ' },
-                       SCO : {start : 35, len : 3, def : ' ' },
-                       REC : {start : 35, len : 3, def : ' ' },
-                       COM : {start : 35, len : 3, def : ' ' },
-               }
-       },
-       MRec : {
-               _8 : {
-                       BKS : {start : 38, len : 1, def : ' ' },
-                       SER : {start : 38, len : 1, def : ' ' },
-                       VIS : {start : 38, len : 1, def : ' ' },
-                       MIX : {start : 38, len : 1, def : ' ' },
-                       MAP : {start : 38, len : 1, def : ' ' },
-                       SCO : {start : 38, len : 1, def : ' ' },
-                       REC : {start : 38, len : 1, def : ' ' },
-                       COM : {start : 38, len : 1, def : ' ' },
-               }
-       },
-       DtSt : {
-               _8 : {
-                       BKS : {start : 6, len : 1, def : ' ' },
-                       SER : {start : 6, len : 1, def : 'c' },
-                       VIS : {start : 6, len : 1, def : ' ' },
-                       MIX : {start : 6, len : 1, def : ' ' },
-                       MAP : {start : 6, len : 1, def : ' ' },
-                       SCO : {start : 6, len : 1, def : ' ' },
-                       REC : {start : 6, len : 1, def : ' ' },
-                       COM : {start : 6, len : 1, def : ' ' },
-               }
-       },
-       Type : {
-               ldr : {
-                       BKS : {start : 6, len : 1, def : 'a' },
-                       SER : {start : 6, len : 1, def : 'a' },
-                       VIS : {start : 6, len : 1, def : 'g' },
-                       MIX : {start : 6, len : 1, def : 'p' },
-                       MAP : {start : 6, len : 1, def : 'e' },
-                       SCO : {start : 6, len : 1, def : 'c' },
-                       REC : {start : 6, len : 1, def : 'i' },
-                       COM : {start : 6, len : 1, def : 'm' },
-               }
-       },
-       Ctrl : {
-               ldr : {
-                       BKS : {start : 8, len : 1, def : ' ' },
-                       SER : {start : 8, len : 1, def : ' ' },
-                       VIS : {start : 8, len : 1, def : ' ' },
-                       MIX : {start : 8, len : 1, def : ' ' },
-                       MAP : {start : 8, len : 1, def : ' ' },
-                       SCO : {start : 8, len : 1, def : ' ' },
-                       REC : {start : 8, len : 1, def : ' ' },
-                       COM : {start : 8, len : 1, def : ' ' },
-               }
-       },
-       BLvl : {
-               ldr : {
-                       BKS : {start : 7, len : 1, def : 'm' },
-                       SER : {start : 7, len : 1, def : 's' },
-                       VIS : {start : 7, len : 1, def : 'm' },
-                       MIX : {start : 7, len : 1, def : 'c' },
-                       MAP : {start : 7, len : 1, def : 'm' },
-                       SCO : {start : 7, len : 1, def : 'm' },
-                       REC : {start : 7, len : 1, def : 'm' },
-                       COM : {start : 7, len : 1, def : 'm' },
-               }
-       },
-       Desc : {
-               ldr : {
-                       BKS : {start : 18, len : 1, def : ' ' },
-                       SER : {start : 18, len : 1, def : ' ' },
-                       VIS : {start : 18, len : 1, def : ' ' },
-                       MIX : {start : 18, len : 1, def : ' ' },
-                       MAP : {start : 18, len : 1, def : ' ' },
-                       SCO : {start : 18, len : 1, def : ' ' },
-                       REC : {start : 18, len : 1, def : ' ' },
-                       COM : {start : 18, len : 1, def : ' ' },
-               }
-       },
-       ELvl : {
-               ldr : {
-                       BKS : {start : 17, len : 1, def : ' ' },
-                       SER : {start : 17, len : 1, def : ' ' },
-                       VIS : {start : 17, len : 1, def : ' ' },
-                       MIX : {start : 17, len : 1, def : ' ' },
-                       MAP : {start : 17, len : 1, def : ' ' },
-                       SCO : {start : 17, len : 1, def : ' ' },
-                       REC : {start : 17, len : 1, def : ' ' },
-                       COM : {start : 17, len : 1, def : ' ' },
-               }
-       },
-       TMat : {
-               _8 : {
-                       VIS : {start : 33, len : 1, def : ' ' },
-               },
-               _6 : {
-                       VIS : {start : 16, len : 1, def : ' ' },
-               }
-       },
-       Indx : {
-               _8 : {
-                       BKS : {start : 31, len : 1, def : '0' },
-                       MAP : {start : 31, len : 1, def : '0' },
-               },
-               _6 : {
-                       BKS : {start : 14, len : 1, def : '0' },
-                       MAP : {start : 14, len : 1, def : '0' },
-               }
-       },
-       Date1 : {
-               _8 : {
-                       BKS : {start : 7, len : 4, def : ' ' },
-                       SER : {start : 7, len : 4, def : ' ' },
-                       VIS : {start : 7, len : 4, def : ' ' },
-                       MIX : {start : 7, len : 4, def : ' ' },
-                       MAP : {start : 7, len : 4, def : ' ' },
-                       SCO : {start : 7, len : 4, def : ' ' },
-                       REC : {start : 7, len : 4, def : ' ' },
-                       COM : {start : 7, len : 4, def : ' ' },
-               },
-       },
-       Date2 : {
-               _8 : {
-                       BKS : {start : 11, len : 4, def : ' ' },
-                       SER : {start : 11, len : 4, def : '9' },
-                       VIS : {start : 11, len : 4, def : ' ' },
-                       MIX : {start : 11, len : 4, def : ' ' },
-                       MAP : {start : 11, len : 4, def : ' ' },
-                       SCO : {start : 11, len : 4, def : ' ' },
-                       REC : {start : 11, len : 4, def : ' ' },
-                       COM : {start : 11, len : 4, def : ' ' },
-               },
-       },
-       LitF : {
-               _8 : {
-                       BKS : {start : 33, len : 1, def : '0' },
-               },
-               _6 : {
-                       BKS : {start : 16, len : 1, def : '0' },
-               }
-       },
-       Biog : {
-               _8 : {
-                       BKS : {start : 34, len : 1, def : ' ' },
-               },
-               _6 : {
-                       BKS : {start : 17, len : 1, def : ' ' },
-               }
-       },
-       Ills : {
-               _8 : {
-                       BKS : {start : 18, len : 4, def : ' ' },
-               },
-               _6 : {
-                       BKS : {start : 1, len : 4, def : ' ' },
-               }
-       },
-       Fest : {
-               _8 : {
-                       BKS : {start : 30, len : 1, def : '0' },
-               },
-               _6 : {
-                       BKS : {start : 13, len : 1, def : '0' },
-               }
-       },
-       Conf : {
-               _8 : {
-                       BKS : {start : 24, len : 4, def : ' ' },
-                       SER : {start : 25, len : 3, def : ' ' },
-               },
-               _6 : {
-                       BKS : {start : 7, len : 4, def : ' ' },
-                       SER : {start : 8, len : 3, def : ' ' },
-               }
-       },
-       GPub : {
-               _8 : {
-                       BKS : {start : 28, len : 1, def : ' ' },
-                       SER : {start : 28, len : 1, def : ' ' },
-                       VIS : {start : 28, len : 1, def : ' ' },
-                       MAP : {start : 28, len : 1, def : ' ' },
-                       COM : {start : 28, len : 1, def : ' ' },
-               },
-               _6 : {
-                       BKS : {start : 11, len : 1, def : ' ' },
-                       SER : {start : 11, len : 1, def : ' ' },
-                       VIS : {start : 11, len : 1, def : ' ' },
-                       MAP : {start : 11, len : 1, def : ' ' },
-                       COM : {start : 11, len : 1, def : ' ' },
-               }
-       },
-       Audn : {
-               _8 : {
-                       BKS : {start : 22, len : 1, def : ' ' },
-                       SER : {start : 22, len : 1, def : ' ' },
-                       VIS : {start : 22, len : 1, def : ' ' },
-                       SCO : {start : 22, len : 1, def : ' ' },
-                       REC : {start : 22, len : 1, def : ' ' },
-                       COM : {start : 22, len : 1, def : ' ' },
-               },
-               _6 : {
-                       BKS : {start : 5, len : 1, def : ' ' },
-                       SER : {start : 5, len : 1, def : ' ' },
-                       VIS : {start : 5, len : 1, def : ' ' },
-                       SCO : {start : 5, len : 1, def : ' ' },
-                       REC : {start : 5, len : 1, def : ' ' },
-                       COM : {start : 5, len : 1, def : ' ' },
-               }
-       },
-       Form : {
-               _8 : {
-                       BKS : {start : 23, len : 1, def : ' ' },
-                       SER : {start : 23, len : 1, def : ' ' },
-                       VIS : {start : 29, len : 1, def : ' ' },
-                       MIX : {start : 23, len : 1, def : ' ' },
-                       MAP : {start : 29, len : 1, def : ' ' },
-                       SCO : {start : 23, len : 1, def : ' ' },
-                       REC : {start : 23, len : 1, def : ' ' },
-               },
-               _6 : {
-                       BKS : {start : 6, len : 1, def : ' ' },
-                       SER : {start : 6, len : 1, def : ' ' },
-                       VIS : {start : 12, len : 1, def : ' ' },
-                       MIX : {start : 6, len : 1, def : ' ' },
-                       MAP : {start : 12, len : 1, def : ' ' },
-                       SCO : {start : 6, len : 1, def : ' ' },
-                       REC : {start : 6, len : 1, def : ' ' },
-               }
-       },
-       'S/L' : {
-               _8 : {
-                       SER : {start : 34, len : 1, def : '0' },
-               },
-               _6 : {
-                       SER : {start : 17, len : 1, def : '0' },
-               }
-       },
-       'Alph' : {
-               _8 : {
-                       SER : {start : 33, len : 1, def : ' ' },
-               },
-               _6 : {
-                       SER : {start : 16, len : 1, def : ' ' },
-               }
-       },
+    Ctry : {
+        _8 : {
+            BKS : {start : 15, len : 3, def : ' ' },
+            SER : {start : 15, len : 3, def : ' ' },
+            VIS : {start : 15, len : 3, def : ' ' },
+            MIX : {start : 15, len : 3, def : ' ' },
+            MAP : {start : 15, len : 3, def : ' ' },
+            SCO : {start : 15, len : 3, def : ' ' },
+            REC : {start : 15, len : 3, def : ' ' },
+            COM : {start : 15, len : 3, def : ' ' },
+        }
+    },
+    Lang : {
+        _8 : {
+            BKS : {start : 35, len : 3, def : ' ' },
+            SER : {start : 35, len : 3, def : ' ' },
+            VIS : {start : 35, len : 3, def : ' ' },
+            MIX : {start : 35, len : 3, def : ' ' },
+            MAP : {start : 35, len : 3, def : ' ' },
+            SCO : {start : 35, len : 3, def : ' ' },
+            REC : {start : 35, len : 3, def : ' ' },
+            COM : {start : 35, len : 3, def : ' ' },
+        }
+    },
+    MRec : {
+        _8 : {
+            BKS : {start : 38, len : 1, def : ' ' },
+            SER : {start : 38, len : 1, def : ' ' },
+            VIS : {start : 38, len : 1, def : ' ' },
+            MIX : {start : 38, len : 1, def : ' ' },
+            MAP : {start : 38, len : 1, def : ' ' },
+            SCO : {start : 38, len : 1, def : ' ' },
+            REC : {start : 38, len : 1, def : ' ' },
+            COM : {start : 38, len : 1, def : ' ' },
+        }
+    },
+    DtSt : {
+        _8 : {
+            BKS : {start : 6, len : 1, def : ' ' },
+            SER : {start : 6, len : 1, def : 'c' },
+            VIS : {start : 6, len : 1, def : ' ' },
+            MIX : {start : 6, len : 1, def : ' ' },
+            MAP : {start : 6, len : 1, def : ' ' },
+            SCO : {start : 6, len : 1, def : ' ' },
+            REC : {start : 6, len : 1, def : ' ' },
+            COM : {start : 6, len : 1, def : ' ' },
+        }
+    },
+    Type : {
+        ldr : {
+            BKS : {start : 6, len : 1, def : 'a' },
+            SER : {start : 6, len : 1, def : 'a' },
+            VIS : {start : 6, len : 1, def : 'g' },
+            MIX : {start : 6, len : 1, def : 'p' },
+            MAP : {start : 6, len : 1, def : 'e' },
+            SCO : {start : 6, len : 1, def : 'c' },
+            REC : {start : 6, len : 1, def : 'i' },
+            COM : {start : 6, len : 1, def : 'm' },
+            AUT : {start : 6, len : 1, def : 'z' },
+            MFHD : {start : 6, len : 1, def : 'y' }
+
+        }
+    },
+    Ctrl : {
+        ldr : {
+            BKS : {start : 8, len : 1, def : ' ' },
+            SER : {start : 8, len : 1, def : ' ' },
+            VIS : {start : 8, len : 1, def : ' ' },
+            MIX : {start : 8, len : 1, def : ' ' },
+            MAP : {start : 8, len : 1, def : ' ' },
+            SCO : {start : 8, len : 1, def : ' ' },
+            REC : {start : 8, len : 1, def : ' ' },
+            COM : {start : 8, len : 1, def : ' ' },
+        }
+    },
+    BLvl : {
+        ldr : {
+            BKS : {start : 7, len : 1, def : 'm' },
+            SER : {start : 7, len : 1, def : 's' },
+            VIS : {start : 7, len : 1, def : 'm' },
+            MIX : {start : 7, len : 1, def : 'c' },
+            MAP : {start : 7, len : 1, def : 'm' },
+            SCO : {start : 7, len : 1, def : 'm' },
+            REC : {start : 7, len : 1, def : 'm' },
+            COM : {start : 7, len : 1, def : 'm' },
+        }
+    },
+    Desc : {
+        ldr : {
+            BKS : {start : 18, len : 1, def : ' ' },
+            SER : {start : 18, len : 1, def : ' ' },
+            VIS : {start : 18, len : 1, def : ' ' },
+            MIX : {start : 18, len : 1, def : ' ' },
+            MAP : {start : 18, len : 1, def : ' ' },
+            SCO : {start : 18, len : 1, def : ' ' },
+            REC : {start : 18, len : 1, def : ' ' },
+            COM : {start : 18, len : 1, def : ' ' },
+        }
+    },
+    Item : {
+        ldr : {
+            MFHD : {start : 18, len : 1, def : 'i' }
+        }
+    },
+    ELvl : {
+        ldr : {
+            BKS : {start : 17, len : 1, def : ' ' },
+            SER : {start : 17, len : 1, def : ' ' },
+            VIS : {start : 17, len : 1, def : ' ' },
+            MIX : {start : 17, len : 1, def : ' ' },
+            MAP : {start : 17, len : 1, def : ' ' },
+            SCO : {start : 17, len : 1, def : ' ' },
+            REC : {start : 17, len : 1, def : ' ' },
+            COM : {start : 17, len : 1, def : ' ' },
+            AUT : {start : 17, len : 1, def : 'n' },
+            MFHD : {start : 17, len : 1, def : 'u' }
+        }
+    },
+    TMat : {
+        _8 : {
+            VIS : {start : 33, len : 1, def : ' ' },
+        },
+        _6 : {
+            VIS : {start : 16, len : 1, def : ' ' },
+        }
+    },
+    Indx : {
+        _8 : {
+            BKS : {start : 31, len : 1, def : '0' },
+            MAP : {start : 31, len : 1, def : '0' },
+        },
+        _6 : {
+            BKS : {start : 14, len : 1, def : '0' },
+            MAP : {start : 14, len : 1, def : '0' },
+        }
+    },
+    Date1 : {
+        _8 : {
+            BKS : {start : 7, len : 4, def : ' ' },
+            SER : {start : 7, len : 4, def : ' ' },
+            VIS : {start : 7, len : 4, def : ' ' },
+            MIX : {start : 7, len : 4, def : ' ' },
+            MAP : {start : 7, len : 4, def : ' ' },
+            SCO : {start : 7, len : 4, def : ' ' },
+            REC : {start : 7, len : 4, def : ' ' },
+            COM : {start : 7, len : 4, def : ' ' },
+        },
+    },
+    Date2 : {
+        _8 : {
+            BKS : {start : 11, len : 4, def : ' ' },
+            SER : {start : 11, len : 4, def : '9' },
+            VIS : {start : 11, len : 4, def : ' ' },
+            MIX : {start : 11, len : 4, def : ' ' },
+            MAP : {start : 11, len : 4, def : ' ' },
+            SCO : {start : 11, len : 4, def : ' ' },
+            REC : {start : 11, len : 4, def : ' ' },
+            COM : {start : 11, len : 4, def : ' ' },
+        },
+    },
+    LitF : {
+        _8 : {
+            BKS : {start : 33, len : 1, def : '0' },
+        },
+        _6 : {
+            BKS : {start : 16, len : 1, def : '0' },
+        }
+    },
+    Biog : {
+        _8 : {
+            BKS : {start : 34, len : 1, def : ' ' },
+        },
+        _6 : {
+            BKS : {start : 17, len : 1, def : ' ' },
+        }
+    },
+    Ills : {
+        _8 : {
+            BKS : {start : 18, len : 4, def : ' ' },
+        },
+        _6 : {
+            BKS : {start : 1, len : 4, def : ' ' },
+        }
+    },
+    Fest : {
+        _8 : {
+            BKS : {start : 30, len : 1, def : '0' },
+        },
+        _6 : {
+            BKS : {start : 13, len : 1, def : '0' },
+        }
+    },
+    Conf : {
+        _8 : {
+            BKS : {start : 24, len : 4, def : ' ' },
+            SER : {start : 25, len : 3, def : ' ' },
+        },
+        _6 : {
+            BKS : {start : 7, len : 4, def : ' ' },
+            SER : {start : 8, len : 3, def : ' ' },
+        }
+    },
+    GPub : {
+        _8 : {
+            BKS : {start : 28, len : 1, def : ' ' },
+            SER : {start : 28, len : 1, def : ' ' },
+            VIS : {start : 28, len : 1, def : ' ' },
+            MAP : {start : 28, len : 1, def : ' ' },
+            COM : {start : 28, len : 1, def : ' ' },
+        },
+        _6 : {
+            BKS : {start : 11, len : 1, def : ' ' },
+            SER : {start : 11, len : 1, def : ' ' },
+            VIS : {start : 11, len : 1, def : ' ' },
+            MAP : {start : 11, len : 1, def : ' ' },
+            COM : {start : 11, len : 1, def : ' ' },
+        }
+    },
+    Audn : {
+        _8 : {
+            BKS : {start : 22, len : 1, def : ' ' },
+            SER : {start : 22, len : 1, def : ' ' },
+            VIS : {start : 22, len : 1, def : ' ' },
+            SCO : {start : 22, len : 1, def : ' ' },
+            REC : {start : 22, len : 1, def : ' ' },
+            COM : {start : 22, len : 1, def : ' ' },
+        },
+        _6 : {
+            BKS : {start : 5, len : 1, def : ' ' },
+            SER : {start : 5, len : 1, def : ' ' },
+            VIS : {start : 5, len : 1, def : ' ' },
+            SCO : {start : 5, len : 1, def : ' ' },
+            REC : {start : 5, len : 1, def : ' ' },
+            COM : {start : 5, len : 1, def : ' ' },
+        }
+    },
+    Form : {
+        _8 : {
+            BKS : {start : 23, len : 1, def : ' ' },
+            SER : {start : 23, len : 1, def : ' ' },
+            VIS : {start : 29, len : 1, def : ' ' },
+            MIX : {start : 23, len : 1, def : ' ' },
+            MAP : {start : 29, len : 1, def : ' ' },
+            SCO : {start : 23, len : 1, def : ' ' },
+            REC : {start : 23, len : 1, def : ' ' },
+        },
+        _6 : {
+            BKS : {start : 6, len : 1, def : ' ' },
+            SER : {start : 6, len : 1, def : ' ' },
+            VIS : {start : 12, len : 1, def : ' ' },
+            MIX : {start : 6, len : 1, def : ' ' },
+            MAP : {start : 12, len : 1, def : ' ' },
+            SCO : {start : 6, len : 1, def : ' ' },
+            REC : {start : 6, len : 1, def : ' ' },
+        }
+    },
+    'S/L' : {
+        _8 : {
+            SER : {start : 34, len : 1, def : '0' },
+        },
+        _6 : {
+            SER : {start : 17, len : 1, def : '0' },
+        }
+    },
+    'Alph' : {
+        _8 : {
+            SER : {start : 33, len : 1, def : ' ' },
+        },
+        _6 : {
+            SER : {start : 16, len : 1, def : ' ' },
+        }
+    },
+    "GeoDiv" : {
+        "_8" : {
+            "AUT" : {"start" : 6, "len" : 1, "def" : ' ' }
+        }
+    },
+    "Roman" : {
+        "_8" : {
+            "AUT" : {"start" : 7, "len" : 1, "def" : ' ' }
+        }
+    },
+    "CatLang" : {
+        "_8" : {
+            "AUT" : {"start" : 8, "len" : 1, "def" : ' ' }
+        }
+    },
+    "Kind" : {
+        "_8" : {
+            "AUT" : {"start" : 9, "len" : 1, "def" : ' ' }
+        }
+    },
+    "Rules" : {
+        "_8" : {
+            "AUT" : {"start" : 10, "len" : 1, "def" : ' ' }
+        }
+    },
+    "SHSys" : {
+        "_8" : {
+            "AUT" : {"start" : 11, "len" : 1, "def" : ' ' }
+        }
+    },
+    "SerType" : {
+        "_8" : {
+            "AUT" : {"start" : 12, "len" : 1, "def" : ' ' }
+        }
+    },
+    "SerNum" : {
+        "_8" : {
+            "AUT" : {"start" : 13, "len" : 1, "def" : ' ' }
+        }
+    },
+    "HeadMain" : {
+        "_8" : {
+            "AUT" : {"start" : 14, "len" : 1, "def" : ' ' }
+        }
+    },
+    "HeadSubj" : {
+        "_8" : {
+            "AUT" : {"start" : 15, "len" : 1, "def" : ' ' }
+        }
+    },
+    "HeadSer" : {
+        "_8" : {
+            "AUT" : {"start" : 16, "len" : 1, "def" : ' ' }
+        }
+    },
+    "TypeSubd" : {
+        "_8" : {
+            "AUT" : {"start" : 17, "len" : 1, "def" : ' ' }
+        }
+    },
+    "TypeGov" : {
+        "_8" : {
+            "AUT" : {"start" : 28, "len" : 1, "def" : ' ' }
+        }
+    },
+    "RefEval" : {
+        "_8" : {
+            "AUT" : {"start" : 29, "len" : 1, "def" : ' ' }
+        }
+    },
+    "RecUpd" : {
+        "_8" : {
+            "AUT" : {"start" : 31, "len" : 1, "def" : ' ' }
+        }
+    },
+    "NameDiff" : {
+        "_8" : {
+            "AUT" : {"start" : 32, "len" : 1, "def" : ' ' }
+        }
+    },
+    "Level" : {
+        "_8" : {
+            "AUT" : {"start" : 33, "len" : 1, "def" : ' ' }
+        }
+    },
+    "ModRec" : {
+        "_8" : {
+            "AUT" : {"start" : 38, "len" : 1, "def" : ' ' }
+        }
+    },
+    "CatSrc" : {
+        "_8" : {
+            "AUT" : {"start" : 39, "len" : 1, "def" : ' ' }
+        }
+    }
 };
 
index 1995edf..696a446 100644 (file)
@@ -194,7 +194,7 @@ sub count_linked_bibs {
 
     my $editor = new_editor();
 
-    my $link_count;
+    my $link_count = [];
     my @clean_records;
     for my $auth ( @$records ) {
         # Protection against SQL injection? Might be overkill.
@@ -226,4 +226,124 @@ sub count_linked_bibs {
     return $link_count;
 }
 
+__PACKAGE__->register_method(
+    "method" => "retrieve_acs",
+    "api_name" => "open-ils.cat.authority.control_set.retrieve",
+    "api_level" => 1,
+    "stream" => 1,
+    "argc" => 2,
+    "signature" => {
+        "desc" => q/Retrieve authority.control_set objects with fleshed
+        thesauri and authority fields/,
+        "params" => [
+            {"name" => "limit",  "desc" => "limit (optional; default 15)", "type" => "number"},
+            {"name" => "offset",  "desc" => "offset doptional; default 0)", "type" => "number"},
+            {"name" => "focus",  "desc" => "optionally make sure the acs object with ID matching this value comes at the top of the result set (only works with offset 0)", "type" => "number"}
+        ]
+    }
+);
+
+# XXX I don't think this really needs to be protected by perms, or does it?
+sub retrieve_acs {
+    my $self = shift;
+    my $client = shift;
+
+    my ($limit, $offset, $focus) = map int, @_;
+
+    $limit ||= 15;
+    $offset ||= 0;
+    $focus ||= undef;
+
+    my $e = new_editor;
+    my $order_by = [
+        {"class" => "acs", "field" => "name"}
+    ];
+
+    # Here is the magic that let's us say that a given acsaf
+    # will be our first result.
+    unshift @$order_by, {
+        "class" => "acs", "field" => "id",
+        "transform" => "numeric_eq", "params" => [$focus],
+        "direction" => "desc"
+    } if $focus;
+
+    my $sets = $e->search_authority_control_set([
+        {"id" => {"!=" => undef}}, {
+            "flesh" => 1,
+            "flesh_fields" => {"acs" => [qw/thesauri authority_fields/]},
+            "order_by" => $order_by,
+            "limit" => $limit,
+            "offset" => $offset
+        }
+    ]) or return $e->die_event;
+
+    $e->disconnect;
+
+    $client->respond($_) foreach @$sets;
+    return undef;
+}
+
+__PACKAGE__->register_method(
+    "method" => "retrieve_acsaf",
+    "api_name" => "open-ils.cat.authority.control_set_authority_field.retrieve",
+    "api_level" => 1,
+    "stream" => 1,
+    "argc" => 2,
+    "signature" => {
+        "desc" => q/Retrieve authority.control_set_authority_field objects with
+        fleshed bib_fields and axes/,
+        "params" => [
+            {"name" => "limit",  "desc" => "limit (optional; default 15)", "type" => "number"},
+            {"name" => "offset",  "desc" => "offset (optional; default 0)", "type" => "number"},
+            {"name" => "control_set",  "desc" => "optionally constrain by value of acsaf.control_set field", "type" => "number"},
+            {"name" => "focus", "desc" => "optionally make sure the acsaf object with ID matching this value comes at the top of the result set (only works with offset 0)"}
+        ]
+    }
+);
+
+sub retrieve_acsaf {
+    my $self = shift;
+    my $client = shift;
+
+    my ($limit, $offset, $control_set, $focus) = map int, @_;
+
+    $limit ||= 15;
+    $offset ||= 0;
+    $control_set ||= undef;
+    $focus ||= undef;
+
+    my $e = new_editor;
+    my $where = {
+        "control_set" => ($control_set ? $control_set : {"!=" => undef})
+    };
+    my $order_by = [
+        {"class" => "acsaf", "field" => "main_entry", "direction" => "desc"},
+        {"class" => "acsaf", "field" => "id"}
+    ];
+
+    unshift @$order_by, {
+        "class" => "acsaf", "field" => "id",
+        "transform" => "numeric_eq", "params" => [$focus],
+        "direction" => "desc"
+    } if $focus;
+
+    my $fields = $e->search_authority_control_set_authority_field([
+        $where, {
+            "flesh" => 2,
+            "flesh_fields" => {
+                "acsaf" => ["bib_fields", "axis_maps"],
+                "abaafm" => ["axis"]
+            },
+            "order_by" => $order_by,
+            "limit" => $limit,
+            "offset" => $offset
+        }
+    ]) or return $e->die_event;
+
+    $e->disconnect;
+
+    $client->respond($_) foreach @$fields;
+    return undef;
+}
+
 1;
index 651e9b5..48abee9 100644 (file)
@@ -1560,7 +1560,7 @@ sub print_hold_pull_list_stream {
                 "flesh_fields" => {
                     "ahr" => ["usr", "current_copy"],
                     "au"  => ["card"],
-                    "acp" => ["location", "call_number"],
+                    "acp" => ["location", "call_number", "parts"],
                     "acn" => ["record","prefix","suffix"]
                 }
             }
@@ -2284,11 +2284,13 @@ sub _check_title_hold_is_possible {
                         }
                     },
                     acpl => { field => 'id', filter => { holdable => 't'}, fkey => 'location' },
-                    ccs  => { field => 'id', filter => { holdable => 't'}, fkey => 'status'   }
+                    ccs  => { field => 'id', filter => { holdable => 't'}, fkey => 'status'   },
+                    acpm => { field => 'target_copy', type => 'left' } # ignore part-linked copies
                 }
             }, 
             where => {
-                '+acp' => { circulate => 't', deleted => 'f', holdable => 't', %org_filter }
+                '+acp' => { circulate => 't', deleted => 'f', holdable => 't', %org_filter },
+                '+acpm' => { target_copy => undef } # ignore part-linked copies
             }
         }
     );
@@ -2657,6 +2659,14 @@ sub _check_volume_hold_is_possible {
        my $copies = new_editor->search_asset_copy({call_number => $vol->id, %org_filter});
        $logger->info("checking possibility of volume hold for volume ".$vol->id);
 
+    my $filter_copies = [];
+    for my $copy (@$copies) {
+        # ignore part-mapped copies for regular volume level holds
+        push(@$filter_copies, $copy) unless
+            new_editor->search_asset_copy_part_map({target_copy => $copy->id})->[0];
+    }
+    $copies = $filter_copies;
+
     return (
         0, 0, [
             new OpenILS::Event(
index 37bc763..3dc14d8 100644 (file)
@@ -6,7 +6,7 @@ use OpenILS::Utils::Fieldmapper;
 use OpenILS::Application::AppUtils;
 use XML::LibXML;
 use XML::LibXSLT;
-use OpenILS::Utils::Editor q/:funcs/;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
 use OpenSRF::Utils::Logger qw/$logger/;
 
 use OpenSRF::Utils::JSON;
@@ -60,6 +60,59 @@ __PACKAGE__->register_method(
         note           => "Searches authority data for existing controlled terms and crossrefs",
 );              
 
+sub search_authority_by_simple_normalize_heading {
+    my $self = shift;
+    my $client = shift;
+    my $marcxml = shift;
+    my $controlset = shift;
+
+    my $query = {
+        select => { are => ['id'] },
+        from   => 'are',
+        where  => {
+            deleted => 'f',
+            marc => { 'startwith' => {
+                transform => 'authority.simple_normalize_heading',
+                value     => [ 'authority.simple_normalize_heading' => $marcxml ]
+            }},
+            defined($controlset) ? ( control_set => $controlset ) : ()
+        }
+    };
+
+    $client->respond($_->{id}) for @{ new_editor()->json_query( $query ) };
+    $client->respond_complete;
+}
+__PACKAGE__->register_method(
+        method         => "search_authority_by_simple_normalize_heading",
+        api_name       => "open-ils.search.authority.simple_heading.from_xml",
+        argc           => 1, 
+        stream      => 1,
+        note           => "Searches authority data by main entry using marcxml, returning 'are' ids; params are marcxml and optional control-set-id",
+);
+
+sub search_authority_batch_by_simple_normalize_heading {
+    my $self = shift;
+    my $client = shift;
+    my $search_set = [@_];
+
+    my $m = $self->method_lookup('open-ils.search.authority.simple_heading.from_xml.atomic');
+
+    for my $s ( @$search_set ) {
+        for my $k ( keys %$s ) {
+            $client->respond( { $k => $m->run( $s->{$k}, $k ) } );
+        }
+    }
+
+    $client->respond_complete;
+}
+__PACKAGE__->register_method(
+        method         => "search_authority_batch_by_simple_normalize_heading",
+        api_name       => "open-ils.search.authority.simple_heading.from_xml.batch",
+        argc           => 1, 
+        stream      => 1,
+        note           => "Searches authority data by main entry using marcxml, in control-set batches, returning 'are' ids; params are hashes of { control-set-id => marcxml }",
+);
+
 
 sub crossref_authority {
        my $self = shift;
index b0d4fc8..01212da 100644 (file)
@@ -636,7 +636,7 @@ __PACKAGE__->register_method(
             { desc => 'Biblio record entry Id', type => 'number' }
         ],
         return => {
-            desc => 'True if specified id can be found in biblio.peer_record_copy_map.peer_record.',
+            desc => 'True if specified id can be found in biblio.peer_bib_copy_map.peer_record.',
             type => 'bool'
         }
     }
index 4cec39f..1bf550d 100644 (file)
@@ -506,6 +506,15 @@ sub modify_from_fieldmapper {
 
        action::survey_answer->has_a( question => 'action::survey_question' );
 
+       biblio::peer_bib_copy_map->has_a( target_copy => 'asset::copy' );
+       biblio::peer_bib_copy_map->has_a( peer_record => 'biblio::record_entry' );
+       biblio::peer_bib_copy_map->has_a( peer_type => 'biblio::peer_type' );
+
+       asset::copy_part_map->has_a( target_copy => 'asset::copy' );
+       asset::copy_part_map->has_a( part => 'biblio::monograph_part' );
+
+       biblio::peer_type->has_many( records => 'biblio::record_entry' );
+
        asset::copy_note->has_a( owning_copy => 'asset::copy' );
        asset::copy_note->has_a( creator => 'actor::user' );
 
@@ -514,6 +523,9 @@ sub modify_from_fieldmapper {
 
        asset::copy->has_many( stat_cat_entries => [ 'asset::stat_cat_entry_copy_map' => 'stat_cat_entry' ] );
        asset::copy->has_many( stat_cat_entry_copy_maps => 'asset::stat_cat_entry_copy_map' );
+       asset::copy->has_many( peer_bib_copy_maps => 'biblio::peer_bib_copy_map' );
+
+       asset::copy->has_many( part_maps => 'asset::copy_part_map' );
 
        asset::copy->has_a( call_number => 'asset::call_number' );
        asset::copy->has_a( creator => 'actor::user' );
index 7780cbc..24e484c 100644 (file)
@@ -31,11 +31,11 @@ biblio::peer_type->columns( Essential => qw/id name/ );
 #-------------------------------------------------------------------------------
 
 #-------------------------------------------------------------------------------
-package biblio::peer_record_copy_map;
+package biblio::peer_bib_copy_map;
 use base qw/biblio/;
 
-biblio::peer_record_copy_map->table( 'biblio_peer_record_copy_map' );
-biblio::peer_record_copy_map->columns( Essential => qw/id peer_type peer_record target_copy/ );
+biblio::peer_bib_copy_map->table( 'biblio_peer_bib_copy_map' );
+biblio::peer_bib_copy_map->columns( Essential => qw/id peer_type peer_record target_copy/ );
 #-------------------------------------------------------------------------------
 
 #-------------------------------------------------------------------------------
index 5c6990f..ae9bfda 100644 (file)
@@ -4,6 +4,7 @@
     package asset::copy_part_map;
 
     asset::copy_part_map->table( 'asset.copy_part_map' );
+    asset::copy_part_map->sequence( 'asset.copy_part_map_id_seq' );
 
     #-------------------------------------------------------------------------------
     package biblio::monograph_part;
     biblio::monograph_part->sequence( 'biblio.monograph_part_id_seq' );
 
        #-------------------------------------------------------------------------------
-       package biblio::peer_record_copy_map;
+       package biblio::peer_bib_copy_map;
 
-       biblio::peer_record_copy_map->table( 'biblio.peer_record_copy_map' );
-       biblio::peer_record_copy_map->sequence( 'biblio.peer_record_copy_map_id_seq' );
+       biblio::peer_bib_copy_map->table( 'biblio.peer_bib_copy_map' );
+       biblio::peer_bib_copy_map->sequence( 'biblio.peer_bib_copy_map_id_seq' );
 
        #-------------------------------------------------------------------------------
        package biblio::peer_type;
index c9743e2..ca1e0df 100644 (file)
@@ -1270,7 +1270,8 @@ sub new_hold_copy_targeter {
                                                isTrue($_->location->holdable) && 
                                                isTrue($_->holdable) &&
                                                !isTrue($_->deleted) &&
-                                               (isTrue($hold->mint_condition) ? isTrue($_->mint_condition) : 1)
+                                               (isTrue($hold->mint_condition) ? isTrue($_->mint_condition) : 1) &&
+                                               ($hold->hold_type ne 'P' ? @{ $_->part_maps } == 0 : 1)
                                        } @$all_copies;
 
                        # let 'em know we're still working
index a0a634a..a43abc5 100644 (file)
@@ -36,12 +36,16 @@ use OpenSRF::Utils::Logger qw($logger);
 # ... and this is our OpenILS object (en|de)coder and psuedo-ORM package.
 use OpenILS::Utils::Fieldmapper;
 
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+
+
 our (
   $_parser,
   $_xslt,
   %record_xslt,
   %metarecord_xslt,
   %holdings_data_cache,
+  %authority_browse_axis_cache,
 );
 
 sub child_init {
@@ -883,6 +887,78 @@ Returns a list of the requested org-scoped record IDs held
                }
 );
 
+sub grab_authority_browse_axes {
+    my ($self, $client, $full) = @_;
+
+    unless(scalar(keys(%authority_browse_axis_cache))) {
+        my $axes = new_editor->search_authority_browse_axis([
+            { code => { '<>' => undef } },
+            { flesh => 2, flesh_fields => { aba => ['fields'], acsaf => ['bib_fields','sub_entries'] } }
+        ]);
+        $authority_browse_axis_cache{$_->code} = $_ for (@$axes);
+    }
+
+    if ($full) {
+        return [
+            map { $authority_browse_axis_cache{$_} } sort keys %authority_browse_axis_cache
+        ];
+    } else {
+        return [keys %authority_browse_axis_cache];
+    }
+}
+__PACKAGE__->register_method(
+       method    => 'grab_authority_browse_axes',
+       api_name  => 'open-ils.supercat.authority.browse_axis_list',
+       api_level => 1,
+       argc      => 1,
+       signature =>
+               { desc     => "Returns a list of valid authority browse/startswith axes",
+                 params   => [
+              { name => 'full', desc => 'Optional. If true, return array containing the full object for each axis, sorted by code. Otherwise just return an array of the codes.', type => 'number' }
+          ],
+                 'return' => { desc => 'Axis codes or whole axes, see "full" param', type => 'array' }
+               }
+);
+
+sub axis_authority_browse {
+       my $self = shift;
+       my $client = shift;
+    my $axis = shift;
+
+    $axis =~ s/^authority\.//;
+    $axis =~ s/(\.refs)$//;
+    my $refs = $1;
+
+    return undef unless ( grep { /$axis/ } @{ grab_authority_browse_axes() } );
+
+    my @tags;
+    for my $f (@{$authority_browse_axis_cache{$axis}->fields}) {
+        push @tags, $f->tag;
+        if ($refs) {
+            push @tags, $_->tag for @{$f->sub_entries};
+        }
+    }
+
+    return authority_tag_sf_browse($self, $client, \@tags, 'a', @_); # XXX TODO figure out something more correct for the subfield param
+}
+__PACKAGE__->register_method(
+       method    => 'axis_authority_browse',
+       api_name  => 'open-ils.supercat.authority.browse.by_axis',
+       api_level => 1,
+       argc      => 2,
+       signature =>
+               { desc     => "Returns a list of the requested authority record IDs held",
+                 params   =>
+                       [ { name => 'axis', desc => 'The target axis', type => 'string' },
+                         { name => 'value', desc => 'The target value', type => 'string' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
+                 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
+               }
+);
+
+=pod
+
 sub general_authority_browse {
        my $self = shift;
        my $client = shift;
@@ -1009,6 +1085,8 @@ __PACKAGE__->register_method(
                }
 );
 
+=cut
+
 sub authority_tag_sf_browse {
     my $self = shift;
     my $client = shift;
@@ -1051,11 +1129,8 @@ sub authority_tag_sf_browse {
         my $before = $_storage->request(
             "open-ils.cstore.json_query.atomic",
             { select    => { afr => [qw/record value/] },
-              from      => { 'are', 'afr' },
-              where     => {
-                '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
-                '+are' => { 'deleted' => 'f' }
-              },
+              from      => 'afr',
+              where     => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
               order_by  => { afr => { value => 'desc' } },
               limit     => $before_limit,
               offset    => abs($page) * $page_size - $before_offset,
@@ -1068,11 +1143,8 @@ sub authority_tag_sf_browse {
         my $after = $_storage->request(
             "open-ils.cstore.json_query.atomic",
             { select    => { afr => [qw/record value/] },
-              from      => { 'are', 'afr' },
-              where     => {
-                '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
-                '+are' => { 'deleted' => 'f' }
-              },
+              from      => 'afr',
+              where     => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
               order_by  => { afr => { value => 'asc' } },
               limit     => $after_limit,
               offset    => abs($page) * $page_size - $after_offset,
@@ -1397,6 +1469,45 @@ Returns a list of the requested org-scoped record IDs held
                }
 );
 
+sub axis_authority_startwith {
+       my $self = shift;
+       my $client = shift;
+    my $axis = shift;
+
+    $axis =~ s/^authority\.//;
+    $axis =~ s/(\.refs)$//;
+    my $refs = $1;
+
+    return undef unless ( grep { /$axis/ } @{ grab_authority_browse_axes() } );
+
+    my @tags;
+    for my $f (@{$authority_browse_axis_cache{$axis}->fields}) {
+        push @tags, $f->tag;
+        if ($refs) {
+            push @tags, $_->tag for @{$f->sub_entries};
+        }
+    }
+
+    return authority_tag_sf_startwith($self, $client, \@tags, 'a', @_); # XXX TODO figure out something more correct for the subfield param
+}
+__PACKAGE__->register_method(
+       method    => 'axis_authority_startwith',
+       api_name  => 'open-ils.supercat.authority.startwith.by_axis',
+       api_level => 1,
+       argc      => 2,
+       signature =>
+               { desc     => "Returns a list of the requested authority record IDs held",
+                 params   =>
+                       [ { name => 'axis', desc => 'The target axis', type => 'string' },
+                         { name => 'value', desc => 'The target value', type => 'string' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
+                         { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
+                 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
+               }
+);
+
+=pod
+
 sub general_authority_startwith {
        my $self = shift;
        my $client = shift;
@@ -1412,7 +1523,7 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held",
                  params   =>
                        [ { name => 'value', desc => 'The target title', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
@@ -1427,7 +1538,7 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held",
                  params   =>
                        [ { name => 'value', desc => 'The target author', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
@@ -1442,7 +1553,7 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held",
                  params   =>
                        [ { name => 'value', desc => 'The target subject', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
@@ -1457,7 +1568,7 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held",
                  params   =>
                        [ { name => 'value', desc => 'The target topical subject', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
@@ -1472,7 +1583,7 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
                  params   =>
                        [ { name => 'value', desc => 'The target title', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
@@ -1487,7 +1598,7 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
                  params   =>
                        [ { name => 'value', desc => 'The target author', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
@@ -1502,7 +1613,7 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
                  params   =>
                        [ { name => 'value', desc => 'The target subject', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
@@ -1517,12 +1628,14 @@ __PACKAGE__->register_method(
                { desc     => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
                  params   =>
                        [ { name => 'value', desc => 'The target topical subject', type => 'string' },
-                         { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
+                         { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
                          { name => 'page', desc => 'The page of records retrieved, calculated based on page_size.  Can be positive, negative or 0.', type => 'number' }, ],
                  'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
                }
 );
 
+=cut
+
 sub authority_tag_sf_startwith {
     my $self = shift;
     my $client = shift;
@@ -1560,11 +1673,8 @@ sub authority_tag_sf_startwith {
         my $before = $_storage->request(
             "open-ils.cstore.json_query.atomic",
             { select    => { afr => [qw/record value/] },
-              from      => { 'afr', 'are' },
-              where     => {
-                '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
-                '+are' => { deleted => 'f' }
-              },
+              from      => 'afr',
+              where     => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
               order_by  => { afr => { value => 'desc' } },
               limit     => $ref_limit,
               offset    => $offset,
@@ -1577,11 +1687,8 @@ sub authority_tag_sf_startwith {
         my $after = $_storage->request(
             "open-ils.cstore.json_query.atomic",
             { select    => { afr => [qw/record value/] },
-              from      => { 'afr', 'are' },
-              where     => {
-                '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
-                '+are' => { deleted => 'f' }
-              },
+              from      => 'afr',
+              where     => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
               order_by  => { afr => { value => 'asc' } },
               limit     => $ref_limit,
               offset    => $offset,
@@ -1627,7 +1734,7 @@ Returns a list of the requested authority record IDs held
                                  desc => 'The target string',
                                  type => 'string' },
                                { name => 'page_size',
-                                 desc => 'Count of call numbers to retrieve, default is 9',
+                                 desc => 'Count of call numbers to retrieve, default is 10',
                                  type => 'number' },
                                { name => 'page',
                                  desc => 'The page of call numbers to retrieve, calculated based on page_size.  Can be positive, negative or 0.',
index d3a6ae8..e148565 100644 (file)
@@ -234,7 +234,13 @@ sub child_init {
         }
     }
 
-    for my $basic_axis ( qw/authority.title authority.author authority.subject authority.topic/ ) {
+    my $auth_axes = $supercat
+        ->request("open-ils.supercat.authority.browse_axis_list")
+        ->gather(1);
+
+
+    for my $axis ( @$auth_axes ) {
+        my $basic_axis = 'authority.' . $axis;
         for my $browse_axis ( ($basic_axis, $basic_axis . ".refs") ) {
             {
                 my $__f = 'marcxml';
@@ -529,6 +535,7 @@ sub unapi {
         $type = 'authority' if ($scheme =~ /^authority/o);
         $command = 'retrieve';
         $command = 'browse' if (grep { $scheme eq $_ } qw/call_number title author subject topic authority.title authority.author authority.subject authority.topic series item-age/);
+        $command = 'browse' if ($scheme =~ /^authority/);
     }
 
     if ($paging) {
@@ -1551,15 +1558,26 @@ sub string_browse {
     $string =~ s/\+/ /go;
     $string =~ s/'//go;
 
-    my $tree = $supercat->request(
-        "open-ils.supercat.$axis.browse",
-        $string,
-        (($axis =~ /^authority/) ? () : ($site)),
-        $page_size,
-        $page,
-        $status,
-        $cpLoc
-    )->gather(1);
+    my $tree;
+    if ($axis =~ /^authority/) {
+        $tree = $supercat->request(
+            "open-ils.supercat.authority.browse.by_axis",
+            $axis,
+            $string,
+            $page_size,
+            $page
+        )->gather(1);
+    } else {
+        $tree = $supercat->request(
+            "open-ils.supercat.$axis.browse",
+            $string,
+            $site,
+            $page_size,
+            $page,
+            $status,
+            $cpLoc
+        )->gather(1);
+    }
 
     (my $norm_format = $format) =~ s/(-full|-uris)$//o;
 
@@ -1616,15 +1634,26 @@ sub string_startwith {
     $string =~ s/\+/ /go;
     $string =~ s/'//go;
 
-    my $tree = $supercat->request(
-        "open-ils.supercat.$axis.startwith",
-        $string,
-        (($axis =~ /^authority/) ? () : ($site)),
-        $page_size,
-        $page,
-        $status,
-        $cpLoc
-    )->gather(1);
+    my $tree;
+    if ($axis =~ /^authority/) {
+        $tree = $supercat->request(
+            "open-ils.supercat.authority.startwith.by_axis",
+            $axis,
+            $string,
+            $page_size,
+            $page
+        )->gather(1);
+    } else {
+        $tree = $supercat->request(
+            "open-ils.supercat.$axis.startwith",
+            $string,
+            $site,
+            $page_size,
+            $page,
+            $status,
+            $cpLoc
+        )->gather(1);
+    }
 
     (my $norm_format = $format) =~ s/(-full|-uris)$//o;
 
@@ -2081,7 +2110,11 @@ sub return_auth_response {
         $recs = [ int($term) ];
     } else {
         $recs = $supercat->request(
-            "open-ils.supercat.authority.$qualifier.startwith", $term, $page_size, $page
+            "open-ils.supercat.authority.startwith.by_axis",
+            $qualifier,
+            $term,
+            $page_size,
+            $page
         )->gather(1);
     }
 
index 6276a29..96277db 100644 (file)
@@ -86,7 +86,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0572', :eg_version); -- berick
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0575', :eg_version); -- miker/senator
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
index f48e223..630fece 100644 (file)
@@ -22,18 +22,61 @@ DROP SCHEMA IF EXISTS authority CASCADE;
 BEGIN;
 CREATE SCHEMA authority;
 
+CREATE TABLE authority.control_set (
+    id          SERIAL  PRIMARY KEY,
+    name        TEXT    NOT NULL UNIQUE, -- i18n
+    description TEXT                     -- i18n
+);
+
+CREATE TABLE authority.control_set_authority_field (
+    id          SERIAL  PRIMARY KEY,
+    main_entry  INT     REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    control_set INT     NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    tag         CHAR(3) NOT NULL,
+    sf_list     TEXT    NOT NULL,
+    name        TEXT    NOT NULL, -- i18n
+    description TEXT              -- i18n
+);
+
+CREATE TABLE authority.control_set_bib_field (
+    id              SERIAL  PRIMARY KEY,
+    authority_field INT     NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    tag             CHAR(3) NOT NULL
+);
+
+CREATE TABLE authority.thesaurus (
+    code        TEXT    PRIMARY KEY,     -- MARC21 thesaurus code
+    control_set INT     NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    name        TEXT    NOT NULL UNIQUE, -- i18n
+    description TEXT                     -- i18n
+);
+
+CREATE TABLE authority.browse_axis (
+    code        TEXT    PRIMARY KEY,
+    name        TEXT    UNIQUE NOT NULL, -- i18n
+    sorter      TEXT    REFERENCES config.record_attr_definition (name) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    description TEXT
+);
+
+CREATE TABLE authority.browse_axis_authority_field_map (
+    id          SERIAL  PRIMARY KEY,
+    axis        TEXT    NOT NULL REFERENCES authority.browse_axis (code) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    field       INT     NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
+);
+
 CREATE TABLE authority.record_entry (
-       id              BIGSERIAL       PRIMARY KEY,
-       creator         INT             NOT NULL DEFAULT 1,
-       editor          INT             NOT NULL DEFAULT 1,
-       create_date     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
-       edit_date       TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
-       active          BOOL            NOT NULL DEFAULT TRUE,
-       deleted         BOOL            NOT NULL DEFAULT FALSE,
-       source          INT,
-       marc            TEXT            NOT NULL,
-       last_xact_id    TEXT            NOT NULL,
-       owner           INT
+    id              BIGSERIAL    PRIMARY KEY,
+    create_date     TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT now(),
+    edit_date       TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT now(),
+    creator         INT     NOT NULL DEFAULT 1,
+    editor          INT     NOT NULL DEFAULT 1,
+    active          BOOL    NOT NULL DEFAULT TRUE,
+    deleted         BOOL    NOT NULL DEFAULT FALSE,
+    source          INT,
+    control_set     INT     REFERENCES authority.control_set (id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    marc            TEXT    NOT NULL,
+    last_xact_id    TEXT    NOT NULL,
+    owner           INT
 );
 CREATE INDEX authority_record_entry_creator_idx ON authority.record_entry ( creator );
 CREATE INDEX authority_record_entry_editor_idx ON authority.record_entry ( editor );
@@ -41,7 +84,6 @@ CREATE INDEX authority_record_deleted_idx ON authority.record_entry(deleted) WHE
 CREATE TRIGGER a_marcxml_is_well_formed BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE biblio.check_marcxml_well_formed();
 CREATE TRIGGER b_maintain_901 BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE evergreen.maintain_901();
 CREATE TRIGGER c_maintain_control_numbers BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE maintain_control_numbers();
-CREATE RULE protect_authority_rec_delete AS ON DELETE TO authority.record_entry DO INSTEAD (UPDATE authority.record_entry SET deleted = TRUE WHERE OLD.id = authority.record_entry.id);
 
 CREATE TABLE authority.bib_linking (
     id          BIGSERIAL   PRIMARY KEY,
@@ -52,148 +94,200 @@ CREATE INDEX authority_bl_bib_idx ON authority.bib_linking ( bib );
 CREATE UNIQUE INDEX authority_bl_bib_authority_once_idx ON authority.bib_linking ( authority, bib );
 
 CREATE TABLE authority.record_note (
-       id              BIGSERIAL       PRIMARY KEY,
-       record          BIGINT          NOT NULL REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
-       value           TEXT            NOT NULL,
-       creator         INT             NOT NULL DEFAULT 1,
-       editor          INT             NOT NULL DEFAULT 1,
-       create_date     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now(),
-       edit_date       TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT now()
+    id          BIGSERIAL   PRIMARY KEY,
+    record      BIGINT      NOT NULL REFERENCES authority.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+    value       TEXT        NOT NULL,
+    creator     INT         NOT NULL DEFAULT 1,
+    editor      INT         NOT NULL DEFAULT 1,
+    create_date TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT now(),
+    edit_date   TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT now()
 );
 CREATE INDEX authority_record_note_record_idx ON authority.record_note ( record );
 CREATE INDEX authority_record_note_creator_idx ON authority.record_note ( creator );
 CREATE INDEX authority_record_note_editor_idx ON authority.record_note ( editor );
 
 CREATE TABLE authority.rec_descriptor (
-       id              BIGSERIAL PRIMARY KEY,
-       record          BIGINT,
-       record_status   TEXT,
-       char_encoding   TEXT
+    id              BIGSERIAL PRIMARY KEY,
+    record          BIGINT,
+    record_status   TEXT,
+    encoding_level  TEXT,
+    thesaurus       TEXT
 );
 CREATE INDEX authority_rec_descriptor_record_idx ON authority.rec_descriptor (record);
 
 CREATE TABLE authority.full_rec (
-       id              BIGSERIAL       PRIMARY KEY,
-       record          BIGINT          NOT NULL,
-       tag             CHAR(3)         NOT NULL,
-       ind1            TEXT,
-       ind2            TEXT,
-       subfield        TEXT,
-       value           TEXT            NOT NULL,
-       index_vector    tsvector        NOT NULL
+    id              BIGSERIAL   PRIMARY KEY,
+    record          BIGINT      NOT NULL,
+    tag             CHAR(3)     NOT NULL,
+    ind1            TEXT,
+    ind2            TEXT,
+    subfield        TEXT,
+    value           TEXT        NOT NULL,
+    index_vector    tsvector    NOT NULL
 );
 CREATE INDEX authority_full_rec_record_idx ON authority.full_rec (record);
 CREATE INDEX authority_full_rec_tag_subfield_idx ON authority.full_rec (tag, subfield);
 CREATE INDEX authority_full_rec_tag_part_idx ON authority.full_rec (SUBSTRING(tag FROM 2));
 CREATE INDEX authority_full_rec_subfield_a_idx ON authority.full_rec (value) WHERE subfield = 'a';
 CREATE TRIGGER authority_full_rec_fti_trigger
-       BEFORE UPDATE OR INSERT ON authority.full_rec
-       FOR EACH ROW EXECUTE PROCEDURE tsearch2(index_vector, value);
+    BEFORE UPDATE OR INSERT ON authority.full_rec
+    FOR EACH ROW EXECUTE PROCEDURE tsearch2(index_vector, value);
 
 CREATE INDEX authority_full_rec_index_vector_idx ON authority.full_rec USING GIST (index_vector);
 /* Enable LIKE to use an index for database clusters with locales other than C or POSIX */
 CREATE INDEX authority_full_rec_value_tpo_index ON authority.full_rec (value text_pattern_ops);
+/* But we still need this (boooo) for paging using >, <, etc */
+CREATE INDEX authority_full_rec_value_index ON authority.full_rec (value);
 
-CREATE OR REPLACE VIEW authority.tracing_links AS
-       SELECT  main.record AS record,
-               main.id AS main_id,
-               main.tag AS main_tag,
-               main.value AS main_value,
-               substr(link.value,1,1) AS relationship,
-               substr(link.value,2,1) AS use_restriction,
-               substr(link.value,3,1) AS deprecation,
-               substr(link.value,4,1) AS display_restriction,
-               link_value.id AS link_id,
-               link_value.tag AS link_tag,
-               link_value.value AS link_value
-         FROM  authority.full_rec main
-               JOIN authority.full_rec link
-                       ON (    link.record = main.record
-                               AND link.tag in ((main.tag::int + 400)::text, (main.tag::int + 300)::text)
-                               AND link.subfield = 'w' )
-               JOIN authority.full_rec link_value
-                       ON (    link_value.record = main.record
-                               AND link_value.tag = link.tag
-                               AND link_value.subfield = 'a' )
-         WHERE main.tag IN ('100','110','111','130','150','151','155','180','181','182','185')
-               AND main.subfield = 'a';
-
--- Function to generate an ephemeral overlay template from an authority record
-CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( TEXT, BIGINT ) RETURNS TEXT AS $func$
-
-    use MARC::Record;
-    use MARC::File::XML (BinaryEncoding => 'UTF-8');
-    use MARC::Charset;
-
-    MARC::Charset->assume_unicode(1);
-
-    my $xml = shift;
-    my $r = MARC::Record->new_from_xml( $xml );
-
-    return undef unless ($r);
+CREATE RULE protect_authority_rec_delete AS ON DELETE TO authority.record_entry DO INSTEAD (UPDATE authority.record_entry SET deleted = TRUE WHERE OLD.id = authority.record_entry.id; DELETE FROM authority.full_rec WHERE record = OLD.id);
 
-    my $id = shift() || $r->subfield( '901' => 'c' );
-    $id =~ s/^\s*(?:\([^)]+\))?\s*(.+)\s*?$/$1/;
-    return undef unless ($id); # We need an ID!
-
-    my $tmpl = MARC::Record->new();
-    $tmpl->encoding( 'UTF-8' );
-
-    my @rule_fields;
-    for my $field ( $r->field( '1..' ) ) { # Get main entry fields from the authority record
-
-        my $tag = $field->tag;
-        my $i1 = $field->indicator(1);
-        my $i2 = $field->indicator(2);
-        my $sf = join '', map { $_->[0] } $field->subfields;
-        my @data = map { @$_ } $field->subfields;
-
-        my @replace_them;
-
-        # Map the authority field to bib fields it can control.
-        if ($tag >= 100 and $tag <= 111) {       # names
-            @replace_them = map { $tag + $_ } (0, 300, 500, 600, 700);
-        } elsif ($tag eq '130') {                # uniform title
-            @replace_them = qw/130 240 440 730 830/;
-        } elsif ($tag >= 150 and $tag <= 155) {  # subjects
-            @replace_them = ($tag + 500);
-        } elsif ($tag >= 180 and $tag <= 185) {  # floating subdivisions
-            @replace_them = qw/100 400 600 700 800 110 410 610 710 810 111 411 611 711 811 130 240 440 730 830 650 651 655/;
-        } else {
-            next;
-        }
-
-        # Dummy up the bib-side data
-        $tmpl->append_fields(
-            map {
-                MARC::Field->new( $_, $i1, $i2, @data )
-            } @replace_them
-        );
-
-        # Construct some 'replace' rules
-        push @rule_fields, map { $_ . $sf . '[0~\)' .$id . '$]' } @replace_them;
-    }
-
-    # Insert the replace rules into the template
-    $tmpl->append_fields(
-        MARC::Field->new( '905' => ' ' => ' ' => 'r' => join(',', @rule_fields ) )
-    );
+-- Intended to be used in a unique index on authority.record_entry like so:
+-- CREATE UNIQUE INDEX unique_by_heading_and_thesaurus
+--   ON authority.record_entry (authority.normalize_heading(marc))
+--   WHERE deleted IS FALSE or deleted = FALSE;
+CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
+DECLARE
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    sf              TEXT;
+    thes_code       TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    tmp_text        TEXT;
+BEGIN
+    thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
+    IF thes_code IS NULL THEN
+        thes_code := '|';
+    END IF;
+
+    SELECT control_set INTO cset FROM authority.thesaurus WHERE code = thes_code;
+    IF NOT FOUND THEN
+        cset = 1;
+    END IF;
+
+    heading_text := '';
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
+        tag_used := acsaf.tag;
+        FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
+            tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
+            IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
+                heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
+            END IF;
+        END LOOP;
+        EXIT WHEN heading_text <> '';
+    END LOOP;
 
-    $xml = $tmpl->as_xml_record;
-    $xml =~ s/^<\?.+?\?>$//mo;
-    $xml =~ s/\n//sgo;
-    $xml =~ s/>\s+</></sgo;
+    IF thes_code = 'z' THEN
+        thes_code := oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml);
+    END IF;
+
+    IF heading_text <> '' THEN
+        IF no_thesaurus IS TRUE THEN
+            heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
+        ELSE
+            heading_text := tag_used || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
+        END IF;
+    ELSE
+        heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
+    END IF;
+
+    RETURN heading_text;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
+    SELECT authority.normalize_heading($1, TRUE);
+$func$ LANGUAGE SQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
+    SELECT authority.normalize_heading($1, FALSE);
+$func$ LANGUAGE SQL IMMUTABLE;
+
+COMMENT ON FUNCTION authority.normalize_heading( TEXT ) IS $$
+Extract the authority heading, thesaurus, and NACO-normalized values
+from an authority record. The primary purpose is to build a unique
+index to defend against duplicated authority records from the same
+thesaurus.
+$$;
+
+-- Adding indexes using oils_xpath_string() for the main entry tags described in
+-- authority.control_set_authority_field would speed this up, if we ever want to use it, though
+-- the existing index on authority.normalize_heading() helps already with a record in hand
+CREATE OR REPLACE VIEW authority.tracing_links AS
+    SELECT  main.record AS record,
+            main.id AS main_id,
+            main.tag AS main_tag,
+            oils_xpath_string('//*[@tag="'||main.tag||'"]/*[local-name()="subfield"]', are.marc) AS main_value,
+            substr(link.value,1,1) AS relationship,
+            substr(link.value,2,1) AS use_restriction,
+            substr(link.value,3,1) AS deprecation,
+            substr(link.value,4,1) AS display_restriction,
+            link.id AS link_id,
+            link.tag AS link_tag,
+            oils_xpath_string('//*[@tag="'||link.tag||'"]/*[local-name()="subfield"]', are.marc) AS link_value,
+            authority.normalize_heading(are.marc) AS normalized_main_value,
+      FROM  authority.full_rec main
+            JOIN authority.record_entry are ON (main.record = are.id)
+            JOIN authority.control_set_authority_field main_entry
+                ON (main_entry.tag = main.tag
+                    AND main_entry.main_entry IS NULL
+                    AND main.subfield = 'a' )
+            JOIN authority.control_set_authority_field sub_entry
+                ON (main_entry.id = sub_entry.main_entry)
+            JOIN authority.full_rec link
+                ON (link.record = main.record
+                    AND link.tag = sub_entry.tag
+                    AND link.subfield = 'w' );
 
-    return $xml;
+-- Function to generate an ephemeral overlay template from an authority record
+CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
+DECLARE
+    cset                INT;
+    main_entry          authority.control_set_authority_field%ROWTYPE;
+    bib_field           authority.control_set_bib_field%ROWTYPE;
+    auth_id             INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
+    replace_data        XML[] DEFAULT '{}'::XML[];
+    replace_rules       TEXT[] DEFAULT '{}'::TEXT[];
+    auth_field          XML[];
+BEGIN
+    IF auth_id IS NULL THEN
+        RETURN NULL;
+    END IF;
+
+    -- Default to the LoC controll set
+    SELECT COALESCE(control_set,1) INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
+        auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
+        IF ARRAY_LENGTH(auth_field,1) > 0 THEN
+            FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
+                replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
+                replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
+            END LOOP;
+            EXIT;
+        END IF;
+    END LOOP;
 
-$func$ LANGUAGE PLPERLU;
+    RETURN XMLELEMENT(
+        name record,
+        XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
+        XMLELEMENT( name leader, '00881nam a2200193   4500'),
+        replace_data,
+        XMLELEMENT(
+            name datafield,
+            XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
+            XMLELEMENT(
+                name subfield,
+                XMLATTRIBUTES('r' AS code),
+                ARRAY_TO_STRING(replace_rules,',')
+            )
+        )
+    )::TEXT;
+END;
+$f$ STABLE LANGUAGE PLPGSQL;
 
 CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( BIGINT ) RETURNS TEXT AS $func$
-    SELECT authority.generate_overlay_template( marc, id ) FROM authority.record_entry WHERE id = $1;
-$func$ LANGUAGE SQL;
-
-CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( TEXT ) RETURNS TEXT AS $func$
-    SELECT authority.generate_overlay_template( $1, NULL );
+    SELECT authority.generate_overlay_template( marc ) FROM authority.record_entry WHERE id = $1;
 $func$ LANGUAGE SQL;
 
 CREATE OR REPLACE FUNCTION authority.merge_records ( target_record BIGINT, source_record BIGINT ) RETURNS INT AS $func$
@@ -211,44 +305,49 @@ BEGIN
     --   replaced by the target record
 
     -- 1. Update all bib records with the ID from target_record in their $0
-    FOR bib_rec IN SELECT bre.* FROM biblio.record_entry bre 
-      INNER JOIN authority.bib_linking abl ON abl.bib = bre.id
-      WHERE abl.authority = source_record LOOP
-
-        UPDATE biblio.record_entry
-          SET marc = REGEXP_REPLACE(marc, 
-            E'(<subfield\\s+code="0"\\s*>[^<]*?\\))' || source_record || '<',
-            E'\\1' || target_record || '<', 'g')
+    FOR bib_rec IN
+            SELECT  bre.*
+              FROM  biblio.record_entry bre 
+                    JOIN authority.bib_linking abl ON abl.bib = bre.id
+              WHERE abl.authority = source_record
+        LOOP
+
+        UPDATE  biblio.record_entry
+          SET   marc = REGEXP_REPLACE(
+                    marc,
+                    E'(<subfield\\s+code="0"\\s*>[^<]*?\\))' || source_record || '<',
+                    E'\\1' || target_record || '<',
+                    'g'
+                )
           WHERE id = bib_rec.id;
 
           moved_objects := moved_objects + 1;
     END LOOP;
 
     -- 2. Grab the current value of reingest on same MARC flag
-    SELECT enabled INTO ingest_same
-      FROM config.internal_flag
+    SELECT  enabled INTO ingest_same
+      FROM  config.internal_flag
       WHERE name = 'ingest.reingest.force_on_same_marc'
     ;
 
     -- 3. Temporarily set reingest on same to TRUE
-    UPDATE config.internal_flag
-      SET enabled = TRUE
+    UPDATE  config.internal_flag
+      SET   enabled = TRUE
       WHERE name = 'ingest.reingest.force_on_same_marc'
     ;
 
     -- 4. Make a harmless update to target_record to trigger auto-update
     --    in linked bibliographic records
-    UPDATE authority.record_entry
-      SET deleted = FALSE
+    UPDATE  authority.record_entry
+      SET   deleted = FALSE
       WHERE id = target_record;
 
     -- 5. "Delete" source_record
-    DELETE FROM authority.record_entry
-      WHERE id = source_record;
+    DELETE FROM authority.record_entry WHERE id = source_record;
 
     -- 6. Set "reingest on same MARC" flag back to initial value
-    UPDATE config.internal_flag
-      SET enabled = ingest_same
+    UPDATE  config.internal_flag
+      SET   enabled = ingest_same
       WHERE name = 'ingest.reingest.force_on_same_marc'
     ;
 
index 8095e3b..9906804 100644 (file)
@@ -979,7 +979,7 @@ CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT
                         if (exists($fields{$f}{match})) {
                             next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
                         }
-                        my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}{sf}};
+                        my @new_sf = map { ($_ => $from_field->subfield($_)) } grep { defined($from_field->subfield($_)) } @{$fields{$f}{sf}};
                         $to_field->add_subfields( @new_sf );
                     }
                 }
index f29239e..c086a01 100644 (file)
@@ -327,96 +327,6 @@ Search "up" the org_unit tree until we find the first occurrence of an
 org_unit_setting with the given name.
 $$;
 
--- Intended to be used in a unique index on authority.record_entry like so:
--- CREATE UNIQUE INDEX unique_by_heading_and_thesaurus
---   ON authority.record_entry (authority.normalize_heading(marc))
---   WHERE deleted IS FALSE or deleted = FALSE;
-CREATE OR REPLACE FUNCTION authority.normalize_heading( TEXT ) RETURNS TEXT AS $func$
-    use strict;
-    use warnings;
-
-    use utf8;
-    use MARC::Record;
-    use MARC::File::XML (BinaryEncoding => 'UTF8');
-    use MARC::Charset;
-    use UUID::Tiny ':std';
-
-    MARC::Charset->assume_unicode(1);
-
-    my $xml = shift() or return undef;
-
-    my $r;
-
-    # Prevent errors in XML parsing from blowing out ungracefully
-    eval {
-        $r = MARC::Record->new_from_xml( $xml );
-        1;
-    } or do {
-       return 'BAD_MARCXML_' . create_uuid_as_string(UUID_MD5, $xml);
-    };
-
-    if (!$r) {
-       return 'BAD_MARCXML_' . create_uuid_as_string(UUID_MD5, $xml);
-    }
-
-    # From http://www.loc.gov/standards/sourcelist/subject.html
-    my $thes_code_map = {
-        a => 'lcsh',
-        b => 'lcshac',
-        c => 'mesh',
-        d => 'nal',
-        k => 'cash',
-        n => 'notapplicable',
-        r => 'aat',
-        s => 'sears',
-        v => 'rvm',
-    };
-
-    # Default to "No attempt to code" if the leader is horribly broken
-    my $fixed_field = $r->field('008');
-    my $thes_char = '|';
-    if ($fixed_field) { 
-        $thes_char = substr($fixed_field->data(), 11, 1) || '|';
-    }
-
-    my $thes_code = 'UNDEFINED';
-
-    if ($thes_char eq 'z') {
-        # Grab the 040 $f per http://www.loc.gov/marc/authority/ad040.html
-        $thes_code = $r->subfield('040', 'f') || 'UNDEFINED';
-    } elsif ($thes_code_map->{$thes_char}) {
-        $thes_code = $thes_code_map->{$thes_char};
-    }
-
-    my $auth_txt = '';
-    my $head = $r->field('1..');
-    if ($head) {
-        # Concatenate all of these subfields together, prefixed by their code
-        # to prevent collisions along the lines of "Fiction, North Carolina"
-        foreach my $sf ($head->subfields()) {
-            $auth_txt .= '‡' . $sf->[0] . ' ' . $sf->[1];
-        }
-    }
-    
-    if ($auth_txt) {
-        my $stmt = spi_prepare('SELECT public.naco_normalize($1) AS norm_text', 'TEXT');
-        my $result = spi_exec_prepared($stmt, $auth_txt);
-        my $norm_txt = $result->{rows}[0]->{norm_text};
-        spi_freeplan($stmt);
-        undef($stmt);
-        return $head->tag() . "_" . $thes_code . " " . $norm_txt;
-    }
-
-    return 'NOHEADING_' . $thes_code . ' ' . create_uuid_as_string(UUID_MD5, $xml);
-$func$ LANGUAGE 'plperlu' IMMUTABLE;
-
-COMMENT ON FUNCTION authority.normalize_heading( TEXT ) IS $$
-Extract the authority heading, thesaurus, and NACO-normalized values
-from an authority record. The primary purpose is to build a unique
-index to defend against duplicated authority records from the same
-thesaurus.
-$$;
-
 CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
 DECLARE
     cur_barcode TEXT;
@@ -505,3 +415,4 @@ Given user input, find an appropriate barcode in the proper class.
 
 Will add prefix/suffix information to do so, and return all results.
 $$;
+
index b70fae4..f8c3d16 100644 (file)
@@ -451,77 +451,6 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-/* Old form of biblio.flatten_marc() relied on contrib/xml2 functions that got all crashy in PostgreSQL 8.4 */
--- CREATE OR REPLACE FUNCTION biblio.flatten_marc ( TEXT, BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
---     SELECT  NULL::bigint AS id, NULL::bigint, 'LDR'::char(3), NULL::TEXT, NULL::TEXT, NULL::TEXT, oils_xpath_string( '//*[local-name()="leader"]', $1 ), NULL::tsvector AS index_vector
---         UNION
---     SELECT  NULL::bigint AS id, NULL::bigint, x.tag::char(3), NULL::TEXT, NULL::TEXT, NULL::TEXT, x.value, NULL::tsvector AS index_vector
---       FROM  oils_xpath_table(
---                 'id',
---                 'marc',
---                 'biblio.record_entry',
---                 '//*[local-name()="controlfield"]/@tag|//*[local-name()="controlfield"]',
---                 'id=' || $2::TEXT
---             )x(record int, tag text, value text)
---         UNION
---     SELECT  NULL::bigint AS id, NULL::bigint, x.tag::char(3), x.ind1, x.ind2, x.subfield, x.value, NULL::tsvector AS index_vector
---       FROM  oils_xpath_table(
---                 'id',
---                 'marc',
---                 'biblio.record_entry',
---                 '//*[local-name()="datafield"]/@tag|' ||
---                 '//*[local-name()="datafield"]/@ind1|' ||
---                 '//*[local-name()="datafield"]/@ind2|' ||
---                 '//*[local-name()="datafield"]/*/@code|' ||
---                 '//*[local-name()="datafield"]/*[@code]',
---                 'id=' || $2::TEXT
---             )x(record int, tag text, ind1 text, ind2 text, subfield text, value text);
--- $func$ LANGUAGE SQL;
-
-CREATE OR REPLACE FUNCTION biblio.flatten_marc ( TEXT ) RETURNS SETOF metabib.full_rec AS $func$
-
-use MARC::Record;
-use MARC::File::XML (BinaryEncoding => 'UTF-8');
-use MARC::Charset;
-
-MARC::Charset->assume_unicode(1);
-
-my $xml = shift;
-my $r = MARC::Record->new_from_xml( $xml );
-
-return_next( { tag => 'LDR', value => $r->leader } );
-
-for my $f ( $r->fields ) {
-       if ($f->is_control_field) {
-               return_next({ tag => $f->tag, value => $f->data });
-       } else {
-               for my $s ($f->subfields) {
-                       return_next({
-                               tag      => $f->tag,
-                               ind1     => $f->indicator(1),
-                               ind2     => $f->indicator(2),
-                               subfield => $s->[0],
-                               value    => $s->[1]
-                       });
-
-                       if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
-                               my $trim = $f->indicator(2) || 0;
-                               return_next({
-                                       tag      => 'tnf',
-                                       ind1     => $f->indicator(1),
-                                       ind2     => $f->indicator(2),
-                                       subfield => 'a',
-                                       value    => substr( $s->[1], $trim )
-                               });
-                       }
-               }
-       }
-}
-
-return undef;
-
-$func$ LANGUAGE PLPERLU;
-
 CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
 DECLARE
        ldr         TEXT;
index 79de913..ebea8cd 100644 (file)
@@ -283,7 +283,9 @@ BEGIN
 
                     PERFORM 1
                       FROM  asset.call_number cn
+                            JOIN asset.copy cp ON (cp.call_number = cn.id)
                       WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND NOT cp.deleted
                       LIMIT 1;
 
                     IF FOUND THEN
index ad05dd7..4395c64 100644 (file)
@@ -120,5 +120,6 @@ ALTER TABLE config.org_unit_setting_type ADD CONSTRAINT update_perm_fkey FOREIGN
 ALTER TABLE config.barcode_completion ADD CONSTRAINT config_barcode_completion_org_unit_fkey FOREIGN KEY (org_unit) REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 
 CREATE INDEX by_heading_and_thesaurus ON authority.record_entry (authority.normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
+CREATE INDEX by_heading ON authority.record_entry (authority.simple_normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
 
 COMMIT;
index c7372ed..0082b83 100644 (file)
@@ -95,6 +95,8 @@ INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath )
     (26, 'identifier', 'tcn', oils_i18n_gettext(26, 'Title Control Number', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='901']/marc:subfield[@code='a']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
     (27, 'identifier', 'bibid', oils_i18n_gettext(27, 'Internal ID', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='901']/marc:subfield[@code='c']$$ );
+INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, facet_field) VALUES
+    (28, 'identifier', 'authority_id', oils_i18n_gettext(28, 'Authority Record ID', 'cmf', 'label'), 'marcxml', '//marc:datafield/marc:subfield[@code="0"]', FALSE, TRUE);
 
 SELECT SETVAL('config.metabib_field_id_seq'::TEXT, (SELECT MAX(id) FROM config.metabib_field), TRUE);
 
@@ -3284,6 +3286,8 @@ INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('MAP',
 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('SCO','cd','abcdmsi');
 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('REC','ij','abcdmsi');
 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('COM','m','abcdmsi');
+INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('AUT','z',' ');
+INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('MFHD','uvxy',' ');
 
 
 ------ Physical Characteristics
@@ -4107,6 +4111,7 @@ INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, leng
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'SCO', 17, 1, ' ');
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'SER', 17, 1, ' ');
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'VIS', 17, 1, ' ');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'AUT', 17, 1, ' ');
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Fest', '006', 'BKS', 13, 1, '0');
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Fest', '008', 'BKS', 30, 1, '0');
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Form', '006', 'BKS', 6, 1, ' ');
@@ -4169,6 +4174,8 @@ INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, leng
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Type', 'ldr', 'SCO', 6, 1, 'c');
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Type', 'ldr', 'SER', 6, 1, 'a');
 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Type', 'ldr', 'VIS', 6, 1, 'g');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Subj', '008', 'AUT', 11, 1, '|');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('RecStat', 'ldr', 'AUT', 5, 1, 'n');
 
 -- record attributes
 INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('alph','Alph','Alph');
@@ -5889,6 +5896,15 @@ INSERT INTO config.metabib_field_index_norm_map (field,norm,params)
       WHERE i.func IN ('replace')
             AND m.id IN (19);
 
+INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
+    SELECT  m.id,
+            i.id,
+            -1
+      FROM  config.metabib_field m,
+            config.index_normalizer i
+      WHERE i.func = 'remove_paren_substring'
+            AND m.id IN (28);
+
 INSERT INTO config.record_attr_index_norm_map (attr,norm,pos)
     SELECT  m.name, i.id, 0
       FROM  config.record_attr_definition m,
@@ -8407,11 +8423,11 @@ INSERT INTO action_trigger.event_definition (
 $$
 [%- USE date -%]
 <style>
-    table { border-collapse: collapse; } 
-    td { padding: 5px; border-bottom: 1px solid #888; } 
+    table { border-collapse: collapse; }
+    td { padding: 5px; border-bottom: 1px solid #888; }
     th { font-weight: bold; }
 </style>
-[% 
+[%
     # Sort the holds into copy-location buckets
     # In the main print loop, sort each bucket by callnumber before printing
     SET holds_list = [];
@@ -8438,14 +8454,14 @@ $$
             <th>Author</th>
             <th>Shelving Location</th>
             <th>Call Number</th>
-            <th>Barcode</th>
+            <th>Barcode/Part</th>
             <th>Patron</th>
         </tr>
     </thead>
     <tbody>
     [% FOR loc_data IN holds_list  %]
         [% FOR hold_data IN loc_data.sort('callnumber') %]
-            [% 
+            [%
                 SET hold = hold_data.hold;
                 SET copy_data = helpers.get_copy_bib_basics(hold.current_copy.id);
             %]
@@ -8454,7 +8470,11 @@ $$
                 <td>[% copy_data.author | truncate %]</td>
                 <td>[% hold.current_copy.location.name %]</td>
                 <td>[% hold.current_copy.call_number.label %]</td>
-                <td>[% hold.current_copy.barcode %]</td>
+                <td>[% hold.current_copy.barcode %]
+                    [% FOR part IN hold.current_copy.parts %]
+                       [% part.part.label %]
+                    [% END %]
+                </td>
                 <td>[% hold.usr.card.barcode %]</td>
             </tr>
         [% END %]
@@ -8471,7 +8491,9 @@ INSERT INTO action_trigger.environment (
         (35, 'current_copy.location'),
         (35, 'current_copy.call_number'),
         (35, 'usr.card'),
-        (35, 'pickup_lib')
+        (35, 'pickup_lib'),
+        (35, 'current_copy.parts'),
+        (35, 'current_copy.parts.part')
 ;
 
 -- 0386.data.org-setting-patron-clone-copy-addr.sql
@@ -9386,4 +9408,150 @@ INSERT INTO action_trigger.environment ( event_def, path) VALUES (
     ,( 47, 'record.queue.owner')
 ;
 
+SELECT SETVAL('authority.control_set_id_seq'::TEXT, 100);
+SELECT SETVAL('authority.control_set_authority_field_id_seq'::TEXT, 1000);
+SELECT SETVAL('authority.control_set_bib_field_id_seq'::TEXT, 1000);
+
+INSERT INTO authority.control_set (id, name, description) VALUES (
+    1,
+    oils_i18n_gettext('1','LoC','acs','name'),
+    oils_i18n_gettext('1','Library of Congress standard authority record control semantics','acs','description')
+);
+
+INSERT INTO authority.control_set_authority_field (id, control_set, main_entry, tag, sf_list, name) VALUES
+
+-- Main entries
+    (1, 1, NULL, '100', 'abcdefklmnopqrstvxyz', oils_i18n_gettext('1','Heading -- Personal Name','acsaf','name')),
+    (2, 1, NULL, '110', 'abcdefgklmnoprstvxyz', oils_i18n_gettext('2','Heading -- Corporate Name','acsaf','name')),
+    (3, 1, NULL, '111', 'acdefgklnpqstvxyz', oils_i18n_gettext('3','Heading -- Meeting Name','acsaf','name')),
+    (4, 1, NULL, '130', 'adfgklmnoprstvxyz', oils_i18n_gettext('4','Heading -- Uniform Title','acsaf','name')),
+    (5, 1, NULL, '150', 'abvxyz', oils_i18n_gettext('5','Heading -- Topical Term','acsaf','name')),
+    (6, 1, NULL, '151', 'avxyz', oils_i18n_gettext('6','Heading -- Geographic Name','acsaf','name')),
+    (7, 1, NULL, '155', 'avxyz', oils_i18n_gettext('7','Heading -- Genre/Form Term','acsaf','name')),
+    (8, 1, NULL, '180', 'vxyz', oils_i18n_gettext('8','Heading -- General Subdivision','acsaf','name')),
+    (9, 1, NULL, '181', 'vxyz', oils_i18n_gettext('9','Heading -- Geographic Subdivision','acsaf','name')),
+    (10, 1, NULL, '182', 'vxyz', oils_i18n_gettext('10','Heading -- Chronological Subdivision','acsaf','name')),
+    (11, 1, NULL, '185', 'vxyz', oils_i18n_gettext('11','Heading -- Form Subdivision','acsaf','name')),
+    (12, 1, NULL, '148', 'avxyz', oils_i18n_gettext('12','Heading -- Chronological Term','acsaf','name')),
+
+-- See Also From tracings
+    (21, 1, 1, '500', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('21','See Also From Tracing -- Personal Name','acsaf','name')),
+    (22, 1, 2, '510', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('22','See Also From Tracing -- Corporate Name','acsaf','name')),
+    (23, 1, 3, '511', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('23','See Also From Tracing -- Meeting Name','acsaf','name')),
+    (24, 1, 4, '530', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('24','See Also From Tracing -- Uniform Title','acsaf','name')),
+    (25, 1, 5, '550', 'abivwxyz4', oils_i18n_gettext('25','See Also From Tracing -- Topical Term','acsaf','name')),
+    (26, 1, 6, '551', 'aivwxyz4', oils_i18n_gettext('26','See Also From Tracing -- Geographic Name','acsaf','name')),
+    (27, 1, 7, '555', 'aivwxyz4', oils_i18n_gettext('27','See Also From Tracing -- Genre/Form Term','acsaf','name')),
+    (28, 1, 8, '580', 'ivwxyz4', oils_i18n_gettext('28','See Also From Tracing -- General Subdivision','acsaf','name')),
+    (29, 1, 9, '581', 'ivwxyz4', oils_i18n_gettext('29','See Also From Tracing -- Geographic Subdivision','acsaf','name')),
+    (30, 1, 10, '582', 'ivwxyz4', oils_i18n_gettext('30','See Also From Tracing -- Chronological Subdivision','acsaf','name')),
+    (31, 1, 11, '585', 'ivwxyz4', oils_i18n_gettext('31','See Also From Tracing -- Form Subdivision','acsaf','name')),
+    (32, 1, 12, '548', 'aivwxyz4', oils_i18n_gettext('32','See Also From Tracing -- Chronological Term','acsaf','name')),
+
+-- Linking entries
+    (41, 1, 1, '700', 'abcdefghjklmnopqrstvwxyz25', oils_i18n_gettext('41','Established Heading Linking Entry -- Personal Name','acsaf','name')),
+    (42, 1, 2, '710', 'abcdefghklmnoprstvwxyz25', oils_i18n_gettext('42','Established Heading Linking Entry -- Corporate Name','acsaf','name')),
+    (43, 1, 3, '711', 'acdefghklnpqstvwxyz25', oils_i18n_gettext('43','Established Heading Linking Entry -- Meeting Name','acsaf','name')),
+    (44, 1, 4, '730', 'adfghklmnoprstvwxyz25', oils_i18n_gettext('44','Established Heading Linking Entry -- Uniform Title','acsaf','name')),
+    (45, 1, 5, '750', 'abvwxyz25', oils_i18n_gettext('45','Established Heading Linking Entry -- Topical Term','acsaf','name')),
+    (46, 1, 6, '751', 'avwxyz25', oils_i18n_gettext('46','Established Heading Linking Entry -- Geographic Name','acsaf','name')),
+    (47, 1, 7, '755', 'avwxyz25', oils_i18n_gettext('47','Established Heading Linking Entry -- Genre/Form Term','acsaf','name')),
+    (48, 1, 8, '780', 'vwxyz25', oils_i18n_gettext('48','Subdivision Linking Entry -- General Subdivision','acsaf','name')),
+    (49, 1, 9, '781', 'vwxyz25', oils_i18n_gettext('49','Subdivision Linking Entry -- Geographic Subdivision','acsaf','name')),
+    (50, 1, 10, '782', 'vwxyz25', oils_i18n_gettext('50','Subdivision Linking Entry -- Chronological Subdivision','acsaf','name')),
+    (51, 1, 11, '785', 'vwxyz25', oils_i18n_gettext('51','Subdivision Linking Entry -- Form Subdivision','acsaf','name')),
+    (52, 1, 12, '748', 'avwxyz25', oils_i18n_gettext('52','Established Heading Linking Entry -- Chronological Term','acsaf','name')),
+
+-- See From tracings
+    (61, 1, 1, '400', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('61','See Also Tracing -- Personal Name','acsaf','name')),
+    (62, 1, 2, '410', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('62','See Also Tracing -- Corporate Name','acsaf','name')),
+    (63, 1, 3, '411', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('63','See Also Tracing -- Meeting Name','acsaf','name')),
+    (64, 1, 4, '430', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('64','See Also Tracing -- Uniform Title','acsaf','name')),
+    (65, 1, 5, '450', 'abivwxyz4', oils_i18n_gettext('65','See Also Tracing -- Topical Term','acsaf','name')),
+    (66, 1, 6, '451', 'aivwxyz4', oils_i18n_gettext('66','See Also Tracing -- Geographic Name','acsaf','name')),
+    (67, 1, 7, '455', 'aivwxyz4', oils_i18n_gettext('67','See Also Tracing -- Genre/Form Term','acsaf','name')),
+    (68, 1, 8, '480', 'ivwxyz4', oils_i18n_gettext('68','See Also Tracing -- General Subdivision','acsaf','name')),
+    (69, 1, 9, '481', 'ivwxyz4', oils_i18n_gettext('69','See Also Tracing -- Geographic Subdivision','acsaf','name')),
+    (70, 1, 10, '482', 'ivwxyz4', oils_i18n_gettext('70','See Also Tracing -- Chronological Subdivision','acsaf','name')),
+    (71, 1, 11, '485', 'ivwxyz4', oils_i18n_gettext('71','See Also Tracing -- Form Subdivision','acsaf','name')),
+    (72, 1, 12, '448', 'aivwxyz4', oils_i18n_gettext('72','See Also Tracing -- Chronological Term','acsaf','name'));
+
+INSERT INTO authority.browse_axis (code,name,description,sorter) VALUES
+    ('title','Title','Title axis','titlesort'),
+    ('author','Author','Author axis','titlesort'),
+    ('subject','Subject','Subject axis','titlesort'),
+    ('topic','Topic','Topic Subject axis','titlesort');
+
+INSERT INTO authority.browse_axis_authority_field_map (axis,field) VALUES
+    ('author',  1 ),
+    ('author',  2 ),
+    ('author',  3 ),
+    ('title',   4 ),
+    ('topic',   5 ),
+    ('subject', 5 ),
+    ('subject', 6 ),
+    ('subject', 7 ),
+    ('subject', 12);
+
+INSERT INTO authority.control_set_bib_field (tag, authority_field) 
+    SELECT '100', id FROM authority.control_set_authority_field WHERE tag IN ('100')
+        UNION
+    SELECT '600', id FROM authority.control_set_authority_field WHERE tag IN ('100','180','181','182','185')
+        UNION
+    SELECT '700', id FROM authority.control_set_authority_field WHERE tag IN ('100')
+        UNION
+    SELECT '800', id FROM authority.control_set_authority_field WHERE tag IN ('100')
+        UNION
+
+    SELECT '110', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+    SELECT '610', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+    SELECT '710', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+    SELECT '810', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+
+    SELECT '111', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+    SELECT '611', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+    SELECT '711', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+    SELECT '811', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+
+    SELECT '130', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '240', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '630', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '730', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '830', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+
+    SELECT '648', id FROM authority.control_set_authority_field WHERE tag IN ('148')
+        UNION
+
+    SELECT '650', id FROM authority.control_set_authority_field WHERE tag IN ('150','180','181','182','185')
+        UNION
+    SELECT '651', id FROM authority.control_set_authority_field WHERE tag IN ('151','180','181','182','185')
+        UNION
+    SELECT '655', id FROM authority.control_set_authority_field WHERE tag IN ('155','180','181','182','185')
+;
+
+INSERT INTO authority.thesaurus (code, name, control_set) VALUES
+    ('a', oils_i18n_gettext('a','Library of Congress Subject Headings','at','name'), 1),
+    ('b', oils_i18n_gettext('b',$$LC subject headings for children's literature$$,'at','name'), 1), -- silly vim '
+    ('c', oils_i18n_gettext('c','Medical Subject Headings','at','name'), 1),
+    ('d', oils_i18n_gettext('d','National Agricultural Library subject authority file','at','name'), 1),
+    ('k', oils_i18n_gettext('k','Canadian Subject Headings','at','name'), 1),
+    ('n', oils_i18n_gettext('n','Not applicable','at','name'), 1),
+    ('r', oils_i18n_gettext('r','Art and Architecture Thesaurus','at','name'), 1),
+    ('s', oils_i18n_gettext('s','Sears List of Subject Headings','at','name'), 1),
+    ('v', oils_i18n_gettext('v','Repertoire de vedettes-matiere','at','name'), 1),
+    ('z', oils_i18n_gettext('z','Other','at','name'), 1),
+    ('|', oils_i18n_gettext('|','No attempt to code','at','name'), 1);
 
index 0faeea6..f0d95e5 100644 (file)
@@ -1378,13 +1378,28 @@ CREATE OR REPLACE FUNCTION authority.propagate_changes (aid BIGINT) RETURNS SETO
     SELECT authority.propagate_changes( authority, bib ) FROM authority.bib_linking WHERE authority = $1;
 $func$ LANGUAGE SQL;
 
--- authority.rec_descriptor appears to be unused currently
+CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
+BEGIN
+    IF NEW.control_set IS NULL THEN
+        SELECT  control_set INTO NEW.control_set
+          FROM  authority.thesaurus
+          WHERE vandelay.marc21_extract_fixed_field(NEW.marc,'Subj') = code;
+    END IF;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
 BEGIN
     DELETE FROM authority.rec_descriptor WHERE record = auth_id;
---    INSERT INTO authority.rec_descriptor (record, record_status, char_encoding)
---        SELECT  auth_id, ;
-
+    INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
+        SELECT  auth_id,
+                vandelay.marc21_extract_fixed_field(marc,'RecStat'),
+                vandelay.marc21_extract_fixed_field(marc,'ELvl'),
+                vandelay.marc21_extract_fixed_field(marc,'Subj')
+          FROM  authority.record_entry
+          WHERE id = auth_id;
     RETURN;
 END;
 $func$ LANGUAGE PLPGSQL;
@@ -1424,11 +1439,10 @@ BEGIN
     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
     IF NOT FOUND THEN
         PERFORM authority.reingest_authority_full_rec(NEW.id);
--- authority.rec_descriptor is not currently used
---        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
---        IF NOT FOUND THEN
---            PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
---        END IF;
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
+        END IF;
     END IF;
 
     RETURN NEW;
@@ -1440,6 +1454,7 @@ CREATE TRIGGER fingerprint_tgr BEFORE INSERT OR UPDATE ON biblio.record_entry FO
 CREATE TRIGGER aaa_indexing_ingest_or_delete AFTER INSERT OR UPDATE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE biblio.indexing_ingest_or_delete ();
 CREATE TRIGGER bbb_simple_rec_trigger AFTER INSERT OR UPDATE OR DELETE ON biblio.record_entry FOR EACH ROW EXECUTE PROCEDURE reporter.simple_rec_trigger ();
 
+CREATE TRIGGER map_thesaurus_to_control_set BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.map_thesaurus_to_control_set ();
 CREATE TRIGGER aaa_auth_ingest_or_delete AFTER INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.indexing_ingest_or_delete ();
 
 -- Utility routines, callable via cstore
diff --git a/Open-ILS/src/sql/Pg/upgrade/0573.schema.staff_search_find_no_copies.sql b/Open-ILS/src/sql/Pg/upgrade/0573.schema.staff_search_find_no_copies.sql
new file mode 100644 (file)
index 0000000..a3f4bab
--- /dev/null
@@ -0,0 +1,318 @@
+-- Evergreen DB patch 0573.schema.staff_search_find_no_copies.sql
+--
+--
+BEGIN;
+
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0573', :eg_version);
+
+CREATE OR REPLACE FUNCTION search.query_parser_fts (
+
+    param_search_ou INT,
+    param_depth     INT,
+    param_query     TEXT,
+    param_statuses  INT[],
+    param_locations INT[],
+    param_offset    INT,
+    param_check     INT,
+    param_limit     INT,
+    metarecord      BOOL,
+    staff           BOOL
+) RETURNS SETOF search.search_result AS $func$
+DECLARE
+
+    current_res         search.search_result%ROWTYPE;
+    search_org_list     INT[];
+
+    check_limit         INT;
+    core_limit          INT;
+    core_offset         INT;
+    tmp_int             INT;
+
+    core_result         RECORD;
+    core_cursor         REFCURSOR;
+    core_rel_query      TEXT;
+
+    total_count         INT := 0;
+    check_count         INT := 0;
+    deleted_count       INT := 0;
+    visible_count       INT := 0;
+    excluded_count      INT := 0;
+
+BEGIN
+
+    check_limit := COALESCE( param_check, 1000 );
+    core_limit  := COALESCE( param_limit, 25000 );
+    core_offset := COALESCE( param_offset, 0 );
+
+    -- core_skip_chk := COALESCE( param_skip_chk, 1 );
+
+    IF param_search_ou > 0 THEN
+        IF param_depth IS NOT NULL THEN
+            SELECT array_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;
+    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;
+    ELSIF param_search_ou = 0 THEN
+        -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
+    END IF;
+
+    OPEN core_cursor FOR EXECUTE param_query;
+
+    LOOP
+
+        FETCH core_cursor INTO core_result;
+        EXIT WHEN NOT FOUND;
+        EXIT WHEN total_count >= core_limit;
+
+        total_count := total_count + 1;
+
+        CONTINUE WHEN total_count NOT BETWEEN  core_offset + 1 AND check_limit + core_offset;
+
+        check_count := check_count + 1;
+
+        PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+        IF NOT FOUND THEN
+            -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
+            deleted_count := deleted_count + 1;
+            CONTINUE;
+        END IF;
+
+        PERFORM 1
+          FROM  biblio.record_entry b
+                JOIN config.bib_source s ON (b.source = s.id)
+          WHERE s.transcendant
+                AND b.id IN ( SELECT * FROM unnest( core_result.records ) );
+
+        IF FOUND THEN
+            -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
+            visible_count := visible_count + 1;
+
+            current_res.id = core_result.id;
+            current_res.rel = core_result.rel;
+
+            tmp_int := 1;
+            IF metarecord THEN
+                SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+            END IF;
+
+            IF tmp_int = 1 THEN
+                current_res.record = core_result.records[1];
+            ELSE
+                current_res.record = NULL;
+            END IF;
+
+            RETURN NEXT current_res;
+
+            CONTINUE;
+        END IF;
+
+        PERFORM 1
+          FROM  asset.call_number cn
+                JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
+                JOIN asset.uri uri ON (map.uri = uri.id)
+          WHERE NOT cn.deleted
+                AND cn.label = '##URI##'
+                AND uri.active
+                AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
+                AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                AND cn.owning_lib IN ( SELECT * FROM unnest( search_org_list ) )
+          LIMIT 1;
+
+        IF FOUND THEN
+            -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
+            visible_count := visible_count + 1;
+
+            current_res.id = core_result.id;
+            current_res.rel = core_result.rel;
+
+            tmp_int := 1;
+            IF metarecord THEN
+                SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+            END IF;
+
+            IF tmp_int = 1 THEN
+                current_res.record = core_result.records[1];
+            ELSE
+                current_res.record = NULL;
+            END IF;
+
+            RETURN NEXT current_res;
+
+            CONTINUE;
+        END IF;
+
+        IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
+
+            PERFORM 1
+              FROM  asset.call_number cn
+                    JOIN asset.copy cp ON (cp.call_number = cn.id)
+              WHERE NOT cn.deleted
+                    AND NOT cp.deleted
+                    AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                    AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                    AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+              LIMIT 1;
+
+            IF NOT FOUND THEN
+                PERFORM 1
+                  FROM  biblio.peer_bib_copy_map pr
+                        JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                  WHERE NOT cp.deleted
+                        AND cp.status IN ( SELECT * FROM unnest( param_statuses ) )
+                        AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records;
+                    excluded_count := excluded_count + 1;
+                    CONTINUE;
+                END IF;
+            END IF;
+
+        END IF;
+
+        IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
+
+            PERFORM 1
+              FROM  asset.call_number cn
+                    JOIN asset.copy cp ON (cp.call_number = cn.id)
+              WHERE NOT cn.deleted
+                    AND NOT cp.deleted
+                    AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                    AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                    AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+              LIMIT 1;
+
+            IF NOT FOUND THEN
+                PERFORM 1
+                  FROM  biblio.peer_bib_copy_map pr
+                        JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                  WHERE NOT cp.deleted
+                        AND cp.location IN ( SELECT * FROM unnest( param_locations ) )
+                        AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+                    -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records;
+                    excluded_count := excluded_count + 1;
+                    CONTINUE;
+                END IF;
+            END IF;
+
+        END IF;
+
+        IF staff IS NULL OR NOT staff THEN
+
+            PERFORM 1
+              FROM  asset.opac_visible_copies
+              WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                    AND record IN ( SELECT * FROM unnest( core_result.records ) )
+              LIMIT 1;
+
+            IF NOT FOUND THEN
+                PERFORM 1
+                  FROM  biblio.peer_bib_copy_map pr
+                        JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy)
+                  WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+
+                    -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                    excluded_count := excluded_count + 1;
+                    CONTINUE;
+                END IF;
+            END IF;
+
+        ELSE
+
+            PERFORM 1
+              FROM  asset.call_number cn
+                    JOIN asset.copy cp ON (cp.call_number = cn.id)
+              WHERE NOT cn.deleted
+                    AND NOT cp.deleted
+                    AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                    AND cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+              LIMIT 1;
+
+            IF NOT FOUND THEN
+
+                PERFORM 1
+                  FROM  biblio.peer_bib_copy_map pr
+                        JOIN asset.copy cp ON (cp.id = pr.target_copy)
+                  WHERE NOT cp.deleted
+                        AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) )
+                        AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) )
+                  LIMIT 1;
+
+                IF NOT FOUND THEN
+
+                    PERFORM 1
+                      FROM  asset.call_number cn
+                            JOIN asset.copy cp ON (cp.call_number = cn.id)
+                      WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) )
+                            AND NOT cp.deleted
+                      LIMIT 1;
+
+                    IF FOUND THEN
+                        -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records;
+                        excluded_count := excluded_count + 1;
+                        CONTINUE;
+                    END IF;
+                END IF;
+
+            END IF;
+
+        END IF;
+
+        visible_count := visible_count + 1;
+
+        current_res.id = core_result.id;
+        current_res.rel = core_result.rel;
+
+        tmp_int := 1;
+        IF metarecord THEN
+            SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
+        END IF;
+
+        IF tmp_int = 1 THEN
+            current_res.record = core_result.records[1];
+        ELSE
+            current_res.record = NULL;
+        END IF;
+
+        RETURN NEXT current_res;
+
+        IF visible_count % 1000 = 0 THEN
+            -- RAISE NOTICE ' % visible so far ... ', visible_count;
+        END IF;
+
+    END LOOP;
+
+    current_res.id = NULL;
+    current_res.rel = NULL;
+    current_res.record = NULL;
+    current_res.total = total_count;
+    current_res.checked = check_count;
+    current_res.deleted = deleted_count;
+    current_res.visible = visible_count;
+    current_res.excluded = excluded_count;
+
+    CLOSE core_cursor;
+
+    RETURN NEXT current_res;
+
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0574.data.hold_pull_list_template.sql b/Open-ILS/src/sql/Pg/upgrade/0574.data.hold_pull_list_template.sql
new file mode 100644 (file)
index 0000000..a0bea8c
--- /dev/null
@@ -0,0 +1,79 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0574', :eg_version);
+
+UPDATE action_trigger.event_definition SET template =
+$$
+[%- USE date -%]
+<style>
+    table { border-collapse: collapse; }
+    td { padding: 5px; border-bottom: 1px solid #888; }
+    th { font-weight: bold; }
+</style>
+[%
+    # Sort the holds into copy-location buckets
+    # In the main print loop, sort each bucket by callnumber before printing
+    SET holds_list = [];
+    SET loc_data = [];
+    SET current_location = target.0.current_copy.location.id;
+    FOR hold IN target;
+        IF current_location != hold.current_copy.location.id;
+            SET current_location = hold.current_copy.location.id;
+            holds_list.push(loc_data);
+            SET loc_data = [];
+        END;
+        SET hold_data = {
+            'hold' => hold,
+            'callnumber' => hold.current_copy.call_number.label
+        };
+        loc_data.push(hold_data);
+    END;
+    holds_list.push(loc_data)
+%]
+<table>
+    <thead>
+        <tr>
+            <th>Title</th>
+            <th>Author</th>
+            <th>Shelving Location</th>
+            <th>Call Number</th>
+            <th>Barcode/Part</th>
+            <th>Patron</th>
+        </tr>
+    </thead>
+    <tbody>
+    [% FOR loc_data IN holds_list  %]
+        [% FOR hold_data IN loc_data.sort('callnumber') %]
+            [%
+                SET hold = hold_data.hold;
+                SET copy_data = helpers.get_copy_bib_basics(hold.current_copy.id);
+            %]
+            <tr>
+                <td>[% copy_data.title | truncate %]</td>
+                <td>[% copy_data.author | truncate %]</td>
+                <td>[% hold.current_copy.location.name %]</td>
+                <td>[% hold.current_copy.call_number.label %]</td>
+                <td>[% hold.current_copy.barcode %]
+                    [% FOR part IN hold.current_copy.parts %]
+                       [% part.part.label %]
+                    [% END %]
+                </td>
+                <td>[% hold.usr.card.barcode %]</td>
+            </tr>
+        [% END %]
+    [% END %]
+    <tbody>
+</table>
+$$
+    WHERE id = 35;
+
+INSERT INTO action_trigger.environment (
+        event_def,
+        path
+    ) VALUES
+        (35, 'current_copy.parts'),
+        (35, 'current_copy.parts.part')
+;
+
+COMMIT;
+
diff --git a/Open-ILS/src/sql/Pg/upgrade/0575.schema.authority-control-sets.sql b/Open-ILS/src/sql/Pg/upgrade/0575.schema.authority-control-sets.sql
new file mode 100644 (file)
index 0000000..c8b74dc
--- /dev/null
@@ -0,0 +1,501 @@
+-- Evergreen DB patch XXXX.schema.authority-control-sets.sql
+--
+-- Schema upgrade to add Authority Control Set functionality
+--
+BEGIN;
+
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0575', :eg_version);
+
+CREATE TABLE authority.control_set (
+    id          SERIAL  PRIMARY KEY,
+    name        TEXT    NOT NULL UNIQUE, -- i18n
+    description TEXT                     -- i18n
+);
+
+CREATE TABLE authority.control_set_authority_field (
+    id          SERIAL  PRIMARY KEY,
+    main_entry  INT     REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    control_set INT     NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    tag         CHAR(3) NOT NULL,
+    sf_list     TEXT    NOT NULL,
+    name        TEXT    NOT NULL, -- i18n
+    description TEXT              -- i18n
+);
+
+CREATE TABLE authority.control_set_bib_field (
+    id              SERIAL  PRIMARY KEY,
+    authority_field INT     NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    tag             CHAR(3) NOT NULL
+);
+
+CREATE TABLE authority.thesaurus (
+    code        TEXT    PRIMARY KEY,     -- MARC21 thesaurus code
+    control_set INT     NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    name        TEXT    NOT NULL UNIQUE, -- i18n
+    description TEXT                     -- i18n
+);
+
+CREATE TABLE authority.browse_axis (
+    code        TEXT    PRIMARY KEY,
+    name        TEXT    UNIQUE NOT NULL, -- i18n
+    sorter      TEXT    REFERENCES config.record_attr_definition (name) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    description TEXT
+);
+
+CREATE TABLE authority.browse_axis_authority_field_map (
+    id          SERIAL  PRIMARY KEY,
+    axis        TEXT    NOT NULL REFERENCES authority.browse_axis (code) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    field       INT     NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
+);
+
+ALTER TABLE authority.record_entry ADD COLUMN control_set INT REFERENCES authority.control_set (id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE authority.rec_descriptor DROP COLUMN char_encoding, ADD COLUMN encoding_level TEXT, ADD COLUMN thesaurus TEXT;
+
+CREATE INDEX authority_full_rec_value_index ON authority.full_rec (value);
+CREATE OR REPLACE RULE protect_authority_rec_delete AS ON DELETE TO authority.record_entry DO INSTEAD (UPDATE authority.record_entry SET deleted = TRUE WHERE OLD.id = authority.record_entry.id; DELETE FROM authority.full_rec WHERE record = OLD.id);
+CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
+DECLARE
+    acsaf           authority.control_set_authority_field%ROWTYPE;
+    tag_used        TEXT;
+    sf              TEXT;
+    thes_code       TEXT;
+    cset            INT;
+    heading_text    TEXT;
+    tmp_text        TEXT;
+BEGIN
+    thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
+    IF thes_code IS NULL THEN
+        thes_code := '|';
+    END IF;
+
+    SELECT control_set INTO cset FROM authority.thesaurus WHERE code = thes_code;
+    IF NOT FOUND THEN
+        cset = 1;
+    END IF;
+
+    heading_text := '';
+    FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
+        tag_used := acsaf.tag;
+        FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
+            tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
+            IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
+                heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
+            END IF;
+        END LOOP;
+        EXIT WHEN heading_text <> '';
+    END LOOP;
+    IF thes_code = 'z' THEN
+        thes_code := oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml);
+    END IF;
+
+    IF heading_text <> '' THEN
+        IF no_thesaurus IS TRUE THEN
+            heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
+        ELSE
+            heading_text := tag_used || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
+        END IF;
+    ELSE
+        heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
+    END IF;
+
+    RETURN heading_text;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
+    SELECT authority.normalize_heading($1, TRUE);
+$func$ LANGUAGE SQL IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
+    SELECT authority.normalize_heading($1, FALSE);
+$func$ LANGUAGE SQL IMMUTABLE;
+
+CREATE OR REPLACE VIEW authority.tracing_links AS
+    SELECT  main.record AS record,
+            main.id AS main_id,
+            main.tag AS main_tag,
+            oils_xpath_string('//*[@tag="'||main.tag||'"]/*[local-name()="subfield"]', are.marc) AS main_value,
+            substr(link.value,1,1) AS relationship,
+            substr(link.value,2,1) AS use_restriction,
+            substr(link.value,3,1) AS deprecation,
+            substr(link.value,4,1) AS display_restriction,
+            link.id AS link_id,
+            link.tag AS link_tag,
+            oils_xpath_string('//*[@tag="'||link.tag||'"]/*[local-name()="subfield"]', are.marc) AS link_value,
+            authority.normalize_heading(are.marc) AS normalized_main_value
+      FROM  authority.full_rec main
+            JOIN authority.record_entry are ON (main.record = are.id)
+            JOIN authority.control_set_authority_field main_entry
+                ON (main_entry.tag = main.tag
+                    AND main_entry.main_entry IS NULL
+                    AND main.subfield = 'a' )
+            JOIN authority.control_set_authority_field sub_entry
+                ON (main_entry.id = sub_entry.main_entry)
+            JOIN authority.full_rec link
+                ON (link.record = main.record
+                    AND link.tag = sub_entry.tag
+                    AND link.subfield = 'w' );
+CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
+DECLARE
+    cset                INT;
+    main_entry          authority.control_set_authority_field%ROWTYPE;
+    bib_field           authority.control_set_bib_field%ROWTYPE;
+    auth_id             INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
+    replace_data        XML[] DEFAULT '{}'::XML[];
+    replace_rules       TEXT[] DEFAULT '{}'::TEXT[];
+    auth_field          XML[];
+BEGIN
+    IF auth_id IS NULL THEN
+        RETURN NULL;
+    END IF;
+
+    -- Default to the LoC controll set
+    SELECT COALESCE(control_set,1) INTO cset FROM authority.record_entry WHERE id = auth_id;
+
+    FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
+        auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
+        IF ARRAY_LENGTH(auth_field,1) > 0 THEN
+            FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
+                replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
+                replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
+            END LOOP;
+            EXIT;
+        END IF;
+    END LOOP;
+    RETURN XMLELEMENT(
+        name record,
+        XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
+        XMLELEMENT( name leader, '00881nam a2200193   4500'),
+        replace_data,
+        XMLELEMENT(
+            name datafield,
+            XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
+            XMLELEMENT(
+                name subfield,
+                XMLATTRIBUTES('r' AS code),
+                ARRAY_TO_STRING(replace_rules,',')
+            )
+        )
+    )::TEXT;
+END;
+$f$ STABLE LANGUAGE PLPGSQL;
+CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( BIGINT ) RETURNS TEXT AS $func$
+    SELECT authority.generate_overlay_template( marc ) FROM authority.record_entry WHERE id = $1;
+$func$ LANGUAGE SQL;
+CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT, force_add INT ) RETURNS TEXT AS $_$
+
+    use MARC::Record;
+    use MARC::File::XML (BinaryEncoding => 'UTF-8');
+    use MARC::Charset;
+    use strict;
+
+    MARC::Charset->assume_unicode(1);
+
+    my $target_xml = shift;
+    my $source_xml = shift;
+    my $field_spec = shift;
+    my $force_add = shift || 0;
+
+    my $target_r = MARC::Record->new_from_xml( $target_xml );
+    my $source_r = MARC::Record->new_from_xml( $source_xml );
+
+    return $target_xml unless ($target_r && $source_r);
+
+    my @field_list = split(',', $field_spec);
+
+    my %fields;
+    for my $f (@field_list) {
+        $f =~ s/^\s*//; $f =~ s/\s*$//;
+        if ($f =~ /^(.{3})(\w*)(?:\[([^]]*)\])?$/) {
+            my $field = $1;
+            $field =~ s/\s+//;
+            my $sf = $2;
+            $sf =~ s/\s+//;
+            my $match = $3;
+            $match =~ s/^\s*//; $match =~ s/\s*$//;
+            $fields{$field} = { sf => [ split('', $sf) ] };
+            if ($match) {
+                my ($msf,$mre) = split('~', $match);
+                if (length($msf) > 0 and length($mre) > 0) {
+                    $msf =~ s/^\s*//; $msf =~ s/\s*$//;
+                    $mre =~ s/^\s*//; $mre =~ s/\s*$//;
+                    $fields{$field}{match} = { sf => $msf, re => qr/$mre/ };
+                }
+            }
+        }
+    }
+
+    for my $f ( keys %fields) {
+        if ( @{$fields{$f}{sf}} ) {
+            for my $from_field ($source_r->field( $f )) {
+                my @tos = $target_r->field( $f );
+                if (!@tos) {
+                    next if (exists($fields{$f}{match}) and !$force_add);
+                    my @new_fields = map { $_->clone } $source_r->field( $f );
+                    $target_r->insert_fields_ordered( @new_fields );
+                } else {
+                    for my $to_field (@tos) {
+                        if (exists($fields{$f}{match})) {
+                            next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
+                        }
+                        my @new_sf = map { ($_ => $from_field->subfield($_)) } grep { defined($from_field->subfield($_)) } @{$fields{$f}{sf}};
+                        $to_field->add_subfields( @new_sf );
+                    }
+                }
+            }
+        } else {
+            my @new_fields = map { $_->clone } $source_r->field( $f );
+            $target_r->insert_fields_ordered( @new_fields );
+        }
+    }
+
+    $target_xml = $target_r->as_xml_record;
+    $target_xml =~ s/^<\?.+?\?>$//mo;
+    $target_xml =~ s/\n//sgo;
+    $target_xml =~ s/>\s+</></sgo;
+
+    return $target_xml;
+
+$_$ LANGUAGE PLPERLU;
+
+
+CREATE INDEX by_heading ON authority.record_entry (authority.simple_normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
+
+INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, facet_field) VALUES
+    (28, 'identifier', 'authority_id', oils_i18n_gettext(28, 'Authority Record ID', 'cmf', 'label'), 'marcxml', '//marc:datafield/marc:subfield[@code="0"]', FALSE, TRUE);
+INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('AUT','z',' ');
+INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('MFHD','uvxy',' ');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'AUT', 17, 1, ' ');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Subj', '008', 'AUT', 11, 1, '|');
+INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('RecStat', 'ldr', 'AUT', 5, 1, 'n');
+INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
+    SELECT  m.id,
+            i.id,
+            -1
+      FROM  config.metabib_field m,
+            config.index_normalizer i
+      WHERE i.func = 'remove_paren_substring'
+            AND m.id IN (28);
+
+SELECT SETVAL('authority.control_set_id_seq'::TEXT, 100);
+SELECT SETVAL('authority.control_set_authority_field_id_seq'::TEXT, 1000);
+SELECT SETVAL('authority.control_set_bib_field_id_seq'::TEXT, 1000);
+
+INSERT INTO authority.control_set (id, name, description) VALUES (
+    1,
+    oils_i18n_gettext('1','LoC','acs','name'),
+    oils_i18n_gettext('1','Library of Congress standard authority record control semantics','acs','description')
+);
+
+INSERT INTO authority.control_set_authority_field (id, control_set, main_entry, tag, sf_list, name) VALUES
+
+-- Main entries
+    (1, 1, NULL, '100', 'abcdefklmnopqrstvxyz', oils_i18n_gettext('1','Heading -- Personal Name','acsaf','name')),
+    (2, 1, NULL, '110', 'abcdefgklmnoprstvxyz', oils_i18n_gettext('2','Heading -- Corporate Name','acsaf','name')),
+    (3, 1, NULL, '111', 'acdefgklnpqstvxyz', oils_i18n_gettext('3','Heading -- Meeting Name','acsaf','name')),
+    (4, 1, NULL, '130', 'adfgklmnoprstvxyz', oils_i18n_gettext('4','Heading -- Uniform Title','acsaf','name')),
+    (5, 1, NULL, '150', 'abvxyz', oils_i18n_gettext('5','Heading -- Topical Term','acsaf','name')),
+    (6, 1, NULL, '151', 'avxyz', oils_i18n_gettext('6','Heading -- Geographic Name','acsaf','name')),
+    (7, 1, NULL, '155', 'avxyz', oils_i18n_gettext('7','Heading -- Genre/Form Term','acsaf','name')),
+    (8, 1, NULL, '180', 'vxyz', oils_i18n_gettext('8','Heading -- General Subdivision','acsaf','name')),
+    (9, 1, NULL, '181', 'vxyz', oils_i18n_gettext('9','Heading -- Geographic Subdivision','acsaf','name')),
+    (10, 1, NULL, '182', 'vxyz', oils_i18n_gettext('10','Heading -- Chronological Subdivision','acsaf','name')),
+    (11, 1, NULL, '185', 'vxyz', oils_i18n_gettext('11','Heading -- Form Subdivision','acsaf','name')),
+    (12, 1, NULL, '148', 'avxyz', oils_i18n_gettext('12','Heading -- Chronological Term','acsaf','name')),
+
+-- See Also From tracings
+    (21, 1, 1, '500', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('21','See Also From Tracing -- Personal Name','acsaf','name')),
+    (22, 1, 2, '510', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('22','See Also From Tracing -- Corporate Name','acsaf','name')),
+    (23, 1, 3, '511', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('23','See Also From Tracing -- Meeting Name','acsaf','name')),
+    (24, 1, 4, '530', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('24','See Also From Tracing -- Uniform Title','acsaf','name')),
+    (25, 1, 5, '550', 'abivwxyz4', oils_i18n_gettext('25','See Also From Tracing -- Topical Term','acsaf','name')),
+    (26, 1, 6, '551', 'aivwxyz4', oils_i18n_gettext('26','See Also From Tracing -- Geographic Name','acsaf','name')),
+    (27, 1, 7, '555', 'aivwxyz4', oils_i18n_gettext('27','See Also From Tracing -- Genre/Form Term','acsaf','name')),
+    (28, 1, 8, '580', 'ivwxyz4', oils_i18n_gettext('28','See Also From Tracing -- General Subdivision','acsaf','name')),
+    (29, 1, 9, '581', 'ivwxyz4', oils_i18n_gettext('29','See Also From Tracing -- Geographic Subdivision','acsaf','name')),
+    (30, 1, 10, '582', 'ivwxyz4', oils_i18n_gettext('30','See Also From Tracing -- Chronological Subdivision','acsaf','name')),
+    (31, 1, 11, '585', 'ivwxyz4', oils_i18n_gettext('31','See Also From Tracing -- Form Subdivision','acsaf','name')),
+    (32, 1, 12, '548', 'aivwxyz4', oils_i18n_gettext('32','See Also From Tracing -- Chronological Term','acsaf','name')),
+
+-- Linking entries
+    (41, 1, 1, '700', 'abcdefghjklmnopqrstvwxyz25', oils_i18n_gettext('41','Established Heading Linking Entry -- Personal Name','acsaf','name')),
+    (42, 1, 2, '710', 'abcdefghklmnoprstvwxyz25', oils_i18n_gettext('42','Established Heading Linking Entry -- Corporate Name','acsaf','name')),
+    (43, 1, 3, '711', 'acdefghklnpqstvwxyz25', oils_i18n_gettext('43','Established Heading Linking Entry -- Meeting Name','acsaf','name')),
+    (44, 1, 4, '730', 'adfghklmnoprstvwxyz25', oils_i18n_gettext('44','Established Heading Linking Entry -- Uniform Title','acsaf','name')),
+    (45, 1, 5, '750', 'abvwxyz25', oils_i18n_gettext('45','Established Heading Linking Entry -- Topical Term','acsaf','name')),
+    (46, 1, 6, '751', 'avwxyz25', oils_i18n_gettext('46','Established Heading Linking Entry -- Geographic Name','acsaf','name')),
+    (47, 1, 7, '755', 'avwxyz25', oils_i18n_gettext('47','Established Heading Linking Entry -- Genre/Form Term','acsaf','name')),
+    (48, 1, 8, '780', 'vwxyz25', oils_i18n_gettext('48','Subdivision Linking Entry -- General Subdivision','acsaf','name')),
+    (49, 1, 9, '781', 'vwxyz25', oils_i18n_gettext('49','Subdivision Linking Entry -- Geographic Subdivision','acsaf','name')),
+    (50, 1, 10, '782', 'vwxyz25', oils_i18n_gettext('50','Subdivision Linking Entry -- Chronological Subdivision','acsaf','name')),
+    (51, 1, 11, '785', 'vwxyz25', oils_i18n_gettext('51','Subdivision Linking Entry -- Form Subdivision','acsaf','name')),
+    (52, 1, 12, '748', 'avwxyz25', oils_i18n_gettext('52','Established Heading Linking Entry -- Chronological Term','acsaf','name')),
+
+-- See From tracings
+    (61, 1, 1, '400', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('61','See Also Tracing -- Personal Name','acsaf','name')),
+    (62, 1, 2, '410', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('62','See Also Tracing -- Corporate Name','acsaf','name')),
+    (63, 1, 3, '411', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('63','See Also Tracing -- Meeting Name','acsaf','name')),
+    (64, 1, 4, '430', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('64','See Also Tracing -- Uniform Title','acsaf','name')),
+    (65, 1, 5, '450', 'abivwxyz4', oils_i18n_gettext('65','See Also Tracing -- Topical Term','acsaf','name')),
+    (66, 1, 6, '451', 'aivwxyz4', oils_i18n_gettext('66','See Also Tracing -- Geographic Name','acsaf','name')),
+    (67, 1, 7, '455', 'aivwxyz4', oils_i18n_gettext('67','See Also Tracing -- Genre/Form Term','acsaf','name')),
+    (68, 1, 8, '480', 'ivwxyz4', oils_i18n_gettext('68','See Also Tracing -- General Subdivision','acsaf','name')),
+    (69, 1, 9, '481', 'ivwxyz4', oils_i18n_gettext('69','See Also Tracing -- Geographic Subdivision','acsaf','name')),
+    (70, 1, 10, '482', 'ivwxyz4', oils_i18n_gettext('70','See Also Tracing -- Chronological Subdivision','acsaf','name')),
+    (71, 1, 11, '485', 'ivwxyz4', oils_i18n_gettext('71','See Also Tracing -- Form Subdivision','acsaf','name')),
+    (72, 1, 12, '448', 'aivwxyz4', oils_i18n_gettext('72','See Also Tracing -- Chronological Term','acsaf','name'));
+
+INSERT INTO authority.browse_axis (code,name,description,sorter) VALUES
+    ('title','Title','Title axis','titlesort'),
+    ('author','Author','Author axis','titlesort'),
+    ('subject','Subject','Subject axis','titlesort'),
+    ('topic','Topic','Topic Subject axis','titlesort');
+
+INSERT INTO authority.browse_axis_authority_field_map (axis,field) VALUES
+    ('author',  1 ),
+    ('author',  2 ),
+    ('author',  3 ),
+    ('title',   4 ),
+    ('topic',   5 ),
+    ('subject', 5 ),
+    ('subject', 6 ),
+    ('subject', 7 ),
+    ('subject', 12);
+
+INSERT INTO authority.control_set_bib_field (tag, authority_field) 
+    SELECT '100', id FROM authority.control_set_authority_field WHERE tag IN ('100')
+        UNION
+    SELECT '600', id FROM authority.control_set_authority_field WHERE tag IN ('100','180','181','182','185')
+        UNION
+    SELECT '700', id FROM authority.control_set_authority_field WHERE tag IN ('100')
+        UNION
+    SELECT '800', id FROM authority.control_set_authority_field WHERE tag IN ('100')
+        UNION
+
+    SELECT '110', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+    SELECT '610', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+    SELECT '710', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+    SELECT '810', id FROM authority.control_set_authority_field WHERE tag IN ('110')
+        UNION
+
+    SELECT '111', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+    SELECT '611', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+    SELECT '711', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+    SELECT '811', id FROM authority.control_set_authority_field WHERE tag IN ('111')
+        UNION
+
+    SELECT '130', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '240', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '630', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '730', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+    SELECT '830', id FROM authority.control_set_authority_field WHERE tag IN ('130')
+        UNION
+
+    SELECT '648', id FROM authority.control_set_authority_field WHERE tag IN ('148')
+        UNION
+
+    SELECT '650', id FROM authority.control_set_authority_field WHERE tag IN ('150','180','181','182','185')
+        UNION
+    SELECT '651', id FROM authority.control_set_authority_field WHERE tag IN ('151','180','181','182','185')
+        UNION
+    SELECT '655', id FROM authority.control_set_authority_field WHERE tag IN ('155','180','181','182','185')
+;
+
+INSERT INTO authority.thesaurus (code, name, control_set) VALUES
+    ('a', oils_i18n_gettext('a','Library of Congress Subject Headings','at','name'), 1),
+    ('b', oils_i18n_gettext('b',$$LC subject headings for children's literature$$,'at','name'), 1), -- silly vim '
+    ('c', oils_i18n_gettext('c','Medical Subject Headings','at','name'), 1),
+    ('d', oils_i18n_gettext('d','National Agricultural Library subject authority file','at','name'), 1),
+    ('k', oils_i18n_gettext('k','Canadian Subject Headings','at','name'), 1),
+    ('n', oils_i18n_gettext('n','Not applicable','at','name'), 1),
+    ('r', oils_i18n_gettext('r','Art and Architecture Thesaurus','at','name'), 1),
+    ('s', oils_i18n_gettext('s','Sears List of Subject Headings','at','name'), 1),
+    ('v', oils_i18n_gettext('v','Repertoire de vedettes-matiere','at','name'), 1),
+    ('z', oils_i18n_gettext('z','Other','at','name'), 1),
+    ('|', oils_i18n_gettext('|','No attempt to code','at','name'), 1);
+CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
+BEGIN
+    IF NEW.control_set IS NULL THEN
+        SELECT  control_set INTO NEW.control_set
+          FROM  authority.thesaurus
+          WHERE vandelay.marc21_extract_fixed_field(NEW.marc,'Subj') = code;
+    END IF;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER map_thesaurus_to_control_set BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.map_thesaurus_to_control_set ();
+
+CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
+BEGIN
+    DELETE FROM authority.rec_descriptor WHERE record = auth_id;
+    INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
+        SELECT  auth_id,
+                vandelay.marc21_extract_fixed_field(marc,'RecStat'),
+                vandelay.marc21_extract_fixed_field(marc,'ELvl'),
+                vandelay.marc21_extract_fixed_field(marc,'Subj')
+          FROM  authority.record_entry
+          WHERE id = auth_id;
+     RETURN;
+ END;
+ $func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+BEGIN
+
+    IF NEW.deleted IS TRUE THEN -- If this authority is deleted
+        DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
+          -- Should remove matching $0 from controlled fields at the same time?
+        RETURN NEW; -- and we're done
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+        -- Propagate these updates to any linked bib records
+        PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
+    END IF;
+
+    -- Flatten and insert the afr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM authority.reingest_authority_full_rec(NEW.id);
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
+        END IF;
+    END IF;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+COMMIT;
+
index 38c7ac8..92d1fd8 100644 (file)
@@ -52,7 +52,7 @@ webcore-install:
        cp @top_srcdir@/Open-ILS/xsl/*.xsl $(DESTDIR)$(XSLDIR)
        cp -r $(DESTDIR)$(WEBDIR)/opac/skin/default/* $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/
        cp -r @top_srcdir@/Open-ILS/web/opac/skin/craftsman/* $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/
-       for i in mresult.xml rresult.xml rdetail.xml advanced.xml myopac.xml cnbrowse.xml; do \
+       for i in mresult.xml rresult.xml rdetail.xml advanced.xml myopac.xml cnbrowse.xml authbrowse.xml; do \
                cd $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/ && rm -f $$i && $(LN_S) index.xml $$i; \
        done
        for i in mresult.xml rresult.xml rdetail.xml advanced.xml myopac.xml cnbrowse.xml; do \
diff --git a/Open-ILS/web/js/dojo/MARC/FixedFields.js b/Open-ILS/web/js/dojo/MARC/FixedFields.js
new file mode 100644 (file)
index 0000000..7e25080
--- /dev/null
@@ -0,0 +1,1665 @@
+/* ---------------------------------------------------------------------------
+ * Copyright (C) 2011  Equinox Software, Inc.
+ * Mike Rylander <miker@esilibrary.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * ---------------------------------------------------------------------------
+ */
+
+if(!dojo._hasResource["MARC.FixedFields"]) {
+
+    dojo.require('MARC.Record');
+
+    dojo._hasResource["MARC.FixedFields"] = true;
+    dojo.provide("MARC.FixedFields");
+
+    MARC.Record._recType = {
+        BKS : { Type : /[at]{1}/,    BLvl : /[acdm]{1}/ },
+        SER : { Type : /[a]{1}/,    BLvl : /[bsi]{1}/ },
+        VIS : { Type : /[gkro]{1}/,    BLvl : /[abcdmsi]{1}/ },
+        MIX : { Type : /[p]{1}/,    BLvl : /[cdi]{1}/ },
+        MAP : { Type : /[ef]{1}/,    BLvl : /[abcdmsi]{1}/ },
+        SCO : { Type : /[cd]{1}/,    BLvl : /[abcdmsi]{1}/ },
+        REC : { Type : /[ij]{1}/,    BLvl : /[abcdmsi]{1}/ },
+        COM : { Type : /[m]{1}/,    BLvl : /[abcdmsi]{1}/ },
+        AUT : { Type : /[z]{1}/,    BLvl : /.{1}/ },
+        MFHD : { Type : /[uvxy]{1}/,  BLvl : /.{1}/ }
+    };
+
+    MARC.Record._ff_pos = {
+        Ctry : {
+            _8 : {
+                BKS : {start : 15, len : 3, def : ' ' },
+                SER : {start : 15, len : 3, def : ' ' },
+                VIS : {start : 15, len : 3, def : ' ' },
+                MIX : {start : 15, len : 3, def : ' ' },
+                MAP : {start : 15, len : 3, def : ' ' },
+                SCO : {start : 15, len : 3, def : ' ' },
+                REC : {start : 15, len : 3, def : ' ' },
+                COM : {start : 15, len : 3, def : ' ' }
+            }
+        },
+        Lang : {
+            _8 : {
+                BKS : {start : 35, len : 3, def : ' ' },
+                SER : {start : 35, len : 3, def : ' ' },
+                VIS : {start : 35, len : 3, def : ' ' },
+                MIX : {start : 35, len : 3, def : ' ' },
+                MAP : {start : 35, len : 3, def : ' ' },
+                SCO : {start : 35, len : 3, def : ' ' },
+                REC : {start : 35, len : 3, def : ' ' },
+                COM : {start : 35, len : 3, def : ' ' }
+            }
+        },
+        MRec : {
+            _8 : {
+                BKS : {start : 38, len : 1, def : ' ' },
+                SER : {start : 38, len : 1, def : ' ' },
+                VIS : {start : 38, len : 1, def : ' ' },
+                MIX : {start : 38, len : 1, def : ' ' },
+                MAP : {start : 38, len : 1, def : ' ' },
+                SCO : {start : 38, len : 1, def : ' ' },
+                REC : {start : 38, len : 1, def : ' ' },
+                COM : {start : 38, len : 1, def : ' ' }
+            }
+        },
+        DtSt : {
+            _8 : {
+                BKS : {start : 6, len : 1, def : ' ' },
+                SER : {start : 6, len : 1, def : 'c' },
+                VIS : {start : 6, len : 1, def : ' ' },
+                MIX : {start : 6, len : 1, def : ' ' },
+                MAP : {start : 6, len : 1, def : ' ' },
+                SCO : {start : 6, len : 1, def : ' ' },
+                REC : {start : 6, len : 1, def : ' ' },
+                COM : {start : 6, len : 1, def : ' ' }
+            }
+        },
+        RecStat : {
+            ldr : {
+                BKS : {start : 5, len : 1, def : 'n' },
+                SER : {start : 5, len : 1, def : 'n' },
+                VIS : {start : 5, len : 1, def : 'n' },
+                MIX : {start : 5, len : 1, def : 'n' },
+                MAP : {start : 5, len : 1, def : 'n' },
+                SCO : {start : 5, len : 1, def : 'n' },
+                REC : {start : 5, len : 1, def : 'n' },
+                COM : {start : 5, len : 1, def : 'n' },
+                MFHD: {start : 5, len : 1, def : 'n' },
+                AUT : {start : 5, len : 1, def : 'n' }
+            }
+        },
+        Type : {
+            ldr : {
+                BKS : {start : 6, len : 1, def : 'a' },
+                SER : {start : 6, len : 1, def : 'a' },
+                VIS : {start : 6, len : 1, def : 'g' },
+                MIX : {start : 6, len : 1, def : 'p' },
+                MAP : {start : 6, len : 1, def : 'e' },
+                SCO : {start : 6, len : 1, def : 'c' },
+                REC : {start : 6, len : 1, def : 'i' },
+                COM : {start : 6, len : 1, def : 'm' },
+                AUT : {start : 6, len : 1, def : 'z' },
+                MFHD : {start : 6, len : 1, def : 'y' }
+    
+            }
+        },
+        Ctrl : {
+            ldr : {
+                BKS : {start : 8, len : 1, def : ' ' },
+                SER : {start : 8, len : 1, def : ' ' },
+                VIS : {start : 8, len : 1, def : ' ' },
+                MIX : {start : 8, len : 1, def : ' ' },
+                MAP : {start : 8, len : 1, def : ' ' },
+                SCO : {start : 8, len : 1, def : ' ' },
+                REC : {start : 8, len : 1, def : ' ' },
+                COM : {start : 8, len : 1, def : ' ' }
+            }
+        },
+        BLvl : {
+            ldr : {
+                BKS : {start : 7, len : 1, def : 'm' },
+                SER : {start : 7, len : 1, def : 's' },
+                VIS : {start : 7, len : 1, def : 'm' },
+                MIX : {start : 7, len : 1, def : 'c' },
+                MAP : {start : 7, len : 1, def : 'm' },
+                SCO : {start : 7, len : 1, def : 'm' },
+                REC : {start : 7, len : 1, def : 'm' },
+                COM : {start : 7, len : 1, def : 'm' }
+            }
+        },
+        Desc : {
+            ldr : {
+                BKS : {start : 18, len : 1, def : ' ' },
+                SER : {start : 18, len : 1, def : ' ' },
+                VIS : {start : 18, len : 1, def : ' ' },
+                MIX : {start : 18, len : 1, def : ' ' },
+                MAP : {start : 18, len : 1, def : ' ' },
+                SCO : {start : 18, len : 1, def : ' ' },
+                REC : {start : 18, len : 1, def : ' ' },
+                COM : {start : 18, len : 1, def : ' ' }
+            }
+        },
+        Item : {
+            ldr : {
+                MFHD : {start : 18, len : 1, def : 'i' }
+            }
+        },
+        ELvl : {
+            ldr : {
+                BKS : {start : 17, len : 1, def : ' ' },
+                SER : {start : 17, len : 1, def : ' ' },
+                VIS : {start : 17, len : 1, def : ' ' },
+                MIX : {start : 17, len : 1, def : ' ' },
+                MAP : {start : 17, len : 1, def : ' ' },
+                SCO : {start : 17, len : 1, def : ' ' },
+                REC : {start : 17, len : 1, def : ' ' },
+                COM : {start : 17, len : 1, def : ' ' },
+                AUT : {start : 17, len : 1, def : 'n' },
+                MFHD : {start : 17, len : 1, def : 'u' }
+            }
+        },
+        TMat : {
+            _8 : {
+                VIS : {start : 33, len : 1, def : ' ' }
+            },
+            _6 : {
+                VIS : {start : 16, len : 1, def : ' ' }
+            }
+        },
+        TrAr : {
+            _8 : {
+                SCO : {start : 33, len : 1, def : ' ' },
+                REC : {start : 33, len : 1, def : 'n' }
+            },
+            _6 : {
+                SCO : {start : 16, len : 1, def : ' ' },
+                REC : {start : 16, len : 1, def : 'n' }
+            }
+        },
+        Indx : {
+            _8 : {
+                BKS : {start : 31, len : 1, def : '0' },
+                MAP : {start : 31, len : 1, def : '0' }
+            },
+            _6 : {
+                BKS : {start : 14, len : 1, def : '0' },
+                MAP : {start : 14, len : 1, def : '0' }
+            }
+        },
+        Date1 : {
+            _8 : {
+                BKS : {start : 7, len : 4, def : ' ' },
+                SER : {start : 7, len : 4, def : ' ' },
+                VIS : {start : 7, len : 4, def : ' ' },
+                MIX : {start : 7, len : 4, def : ' ' },
+                MAP : {start : 7, len : 4, def : ' ' },
+                SCO : {start : 7, len : 4, def : ' ' },
+                REC : {start : 7, len : 4, def : ' ' },
+                COM : {start : 7, len : 4, def : ' ' }
+            }
+        },
+        Date2 : {
+            _8 : {
+                BKS : {start : 11, len : 4, def : ' ' },
+                SER : {start : 11, len : 4, def : '9' },
+                VIS : {start : 11, len : 4, def : ' ' },
+                MIX : {start : 11, len : 4, def : ' ' },
+                MAP : {start : 11, len : 4, def : ' ' },
+                SCO : {start : 11, len : 4, def : ' ' },
+                REC : {start : 11, len : 4, def : ' ' },
+                COM : {start : 11, len : 4, def : ' ' }
+            }
+        },
+        LitF : {
+            _8 : {
+                BKS : {start : 33, len : 1, def : '0' }
+            },
+            _6 : {
+                BKS : {start : 16, len : 1, def : '0' }
+            }
+        },
+        Biog : {
+            _8 : {
+                BKS : {start : 34, len : 1, def : ' ' }
+            },
+            _6 : {
+                BKS : {start : 17, len : 1, def : ' ' }
+            }
+        },
+        Ills : {
+            _8 : {
+                BKS : {start : 18, len : 4, def : ' ' }
+            },
+            _6 : {
+                BKS : {start : 1, len : 4, def : ' ' }
+            }
+        },
+        Fest : {
+            _8 : {
+                BKS : {start : 30, len : 1, def : '0' }
+            },
+            _6 : {
+                BKS : {start : 13, len : 1, def : '0' }
+            }
+        },
+        Conf : {
+            _8 : {
+                BKS : {start : 24, len : 4, def : ' ' },
+                SER : {start : 25, len : 3, def : ' ' }
+            },
+            _6 : {
+                BKS : {start : 7, len : 4, def : ' ' },
+                SER : {start : 8, len : 3, def : ' ' }
+            }
+        },
+        GPub : {
+            _8 : {
+                BKS : {start : 28, len : 1, def : ' ' },
+                SER : {start : 28, len : 1, def : ' ' },
+                VIS : {start : 28, len : 1, def : ' ' },
+                MAP : {start : 28, len : 1, def : ' ' },
+                COM : {start : 28, len : 1, def : ' ' }
+            },
+            _6 : {
+                BKS : {start : 11, len : 1, def : ' ' },
+                SER : {start : 11, len : 1, def : ' ' },
+                VIS : {start : 11, len : 1, def : ' ' },
+                MAP : {start : 11, len : 1, def : ' ' },
+                COM : {start : 11, len : 1, def : ' ' }
+            }
+        },
+        Srce : {
+            _8 : {
+                BKS : {start : 39, len : 1, def : 'd' },
+                SER : {start : 39, len : 1, def : 'd' },
+                VIS : {start : 39, len : 1, def : 'd' },
+                SCO : {start : 39, len : 1, def : 'd' },
+                REC : {start : 39, len : 1, def : 'd' },
+                COM : {start : 39, len : 1, def : 'd' },
+                MFHD : {start : 39, len : 1, def : 'd' },
+                "AUT" : {"start" : 39, "len" : 1, "def" : 'd' }
+            }
+       },
+        Audn : {
+            _8 : {
+                BKS : {start : 22, len : 1, def : ' ' },
+                SER : {start : 22, len : 1, def : ' ' },
+                VIS : {start : 22, len : 1, def : ' ' },
+                SCO : {start : 22, len : 1, def : ' ' },
+                REC : {start : 22, len : 1, def : ' ' },
+                COM : {start : 22, len : 1, def : ' ' }
+            },
+            _6 : {
+                BKS : {start : 5, len : 1, def : ' ' },
+                SER : {start : 5, len : 1, def : ' ' },
+                VIS : {start : 5, len : 1, def : ' ' },
+                SCO : {start : 5, len : 1, def : ' ' },
+                REC : {start : 5, len : 1, def : ' ' },
+                COM : {start : 5, len : 1, def : ' ' }
+            }
+        },
+        Form : {
+            _8 : {
+                BKS : {start : 23, len : 1, def : ' ' },
+                SER : {start : 23, len : 1, def : ' ' },
+                VIS : {start : 29, len : 1, def : ' ' },
+                MIX : {start : 23, len : 1, def : ' ' },
+                MAP : {start : 29, len : 1, def : ' ' },
+                SCO : {start : 23, len : 1, def : ' ' },
+                REC : {start : 23, len : 1, def : ' ' }
+            },
+            _6 : {
+                BKS : {start : 6, len : 1, def : ' ' },
+                SER : {start : 6, len : 1, def : ' ' },
+                VIS : {start : 12, len : 1, def : ' ' },
+                MIX : {start : 6, len : 1, def : ' ' },
+                MAP : {start : 12, len : 1, def : ' ' },
+                SCO : {start : 6, len : 1, def : ' ' },
+                REC : {start : 6, len : 1, def : ' ' }
+            }
+        },
+        'S/L' : {
+            _8 : {
+                SER : {start : 34, len : 1, def : '0' }
+            },
+            _6 : {
+                SER : {start : 17, len : 1, def : '0' }
+            }
+        },
+        'Alph' : {
+            _8 : {
+                SER : {start : 33, len : 1, def : ' ' }
+            },
+            _6 : {
+                SER : {start : 16, len : 1, def : ' ' }
+            }
+        },
+        "GeoSubd" : {
+            "_8" : {
+                "AUT" : {"start" : 6, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Roman" : {
+            "_8" : {
+                "AUT" : {"start" : 7, "len" : 1, "def" : ' ' }
+            }
+        },
+        "CatLang" : {
+            "_8" : {
+                "AUT" : {"start" : 8, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Auth/Ref" : {
+            "_8" : {
+                "AUT" : {"start" : 9, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Rules" : {
+            "_8" : {
+                "AUT" : {"start" : 10, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Subj" : {
+            "_8" : {
+                "AUT" : {"start" : 11, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Series" : {
+            "_8" : {
+                "AUT" : {"start" : 12, "len" : 1, "def" : ' ' }
+            }
+        },
+        "SerNum" : {
+            "_8" : {
+                "AUT" : {"start" : 13, "len" : 1, "def" : ' ' }
+            }
+        },
+        "NameUse" : {
+            "_8" : {
+                "AUT" : {"start" : 14, "len" : 1, "def" : ' ' }
+            }
+        },
+        "SubjUse" : {
+            "_8" : {
+                "AUT" : {"start" : 15, "len" : 1, "def" : ' ' }
+            }
+        },
+        "SerUse" : {
+            "_8" : {
+                "AUT" : {"start" : 16, "len" : 1, "def" : ' ' }
+            }
+        },
+        "TypeSubd" : {
+            "_8" : {
+                "AUT" : {"start" : 17, "len" : 1, "def" : ' ' }
+            }
+        },
+        "GovtAgn" : {
+            "_8" : {
+                "AUT" : {"start" : 28, "len" : 1, "def" : ' ' }
+            }
+        },
+        "RefStatus" : {
+            "_8" : {
+                "AUT" : {"start" : 29, "len" : 1, "def" : ' ' }
+            }
+        },
+        "UpdStatus" : {
+            "_8" : {
+                "AUT" : {"start" : 31, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Name" : {
+            "_8" : {
+                "AUT" : {"start" : 32, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Status" : {
+            "_8" : {
+                "AUT" : {"start" : 33, "len" : 1, "def" : ' ' }
+            }
+        },
+        "ModRec" : {
+            "_8" : {
+                "AUT" : {"start" : 38, "len" : 1, "def" : ' ' }
+            }
+        },
+        "Source" : {
+            "_8" : {
+                "AUT" : {"start" : 39, "len" : 1, "def" : ' ' }
+            }
+        }
+    };
+    
+    MARC.Record._physical_characteristics = {
+       c : {
+               label     : "Electronic Resource",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       a : "Tape Cartridge",
+                                               b : "Chip cartridge",
+                                               c : "Computer optical disk cartridge",
+                                               f : "Tape cassette",
+                                               h : "Tape reel",
+                                               j : "Magnetic disk",
+                                               m : "Magneto-optical disk",
+                                               o : "Optical disk",
+                                               r : "Remote",
+                                               u : "Unspecified",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Color",
+                               values: {       a : "One color",
+                                               b : "Black-and-white",
+                                               c : "Multicolored",
+                                               g : "Gray scale",
+                                               m : "Mixed",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Dimensions",
+                               values: {       a : "3 1/2 in.",
+                                               e : "12 in.",
+                                               g : "4 3/4 in. or 12 cm.",
+                                               i : "1 1/8 x 2 3/8 in.",
+                                               j : "3 7/8 x 2 1/2 in.",
+                                               n : "Not applicable",
+                                               o : "5 1/4 in.",
+                                               u : "Unknown",
+                                               v : "8 in.",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Sound",
+                               values: {       ' ' : "No sound (Silent)",
+                                               a   : "Sound",
+                                               u   : "Unknown"
+                               }
+                       },
+                       g : {   start : 6,
+                               len   : 3,
+                               label : "Image bit depth",
+                               values: {       mmm   : "Multiple",
+                                               nnn   : "Not applicable",
+                                               '---' : "Unknown"
+                               }
+                       },
+                       h : {   start : 9,
+                               len   : 1,
+                               label : "File formats",
+                               values: {       a : "One file format",
+                                               m : "Multiple file formats",
+                                               u : "Unknown"
+                               }
+                       },
+                       i : {   start : 10,
+                               len   : 1,
+                               label : "Quality assurance target(s)",
+                               values: {       a : "Absent",
+                                               n : "Not applicable",
+                                               p : "Present",
+                                               u : "Unknown"
+                               }
+                       },
+                       j : {   start : 11,
+                               len   : 1,
+                               label : "Antecedent/Source",
+                               values: {       a : "File reproduced from original",
+                                               b : "File reproduced from microform",
+                                               c : "File reproduced from electronic resource",
+                                               d : "File reproduced from an intermediate (not microform)",
+                                               m : "Mixed",
+                                               n : "Not applicable",
+                                               u : "Unknown"
+                               }
+                       },
+                       k : {   start : 12,
+                               len   : 1,
+                               label : "Level of compression",
+                               values: {       a : "Uncompressed",
+                                               b : "Lossless",
+                                               d : "Lossy",
+                                               m : "Mixed",
+                                               u : "Unknown"
+                               }
+                       },
+                       l : {   start : 13,
+                               len   : 1,
+                               label : "Reformatting quality",
+                               values: {       a : "Access",
+                                               n : "Not applicable",
+                                               p : "Preservation",
+                                               r : "Replacement",
+                                               u : "Unknown"
+                               }
+                       }
+               }
+       },
+       d : {
+               label     : "Globe",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       a : "Celestial globe",
+                                               b : "Planetary or lunar globe",
+                                               c : "Terrestrial globe",
+                                               e : "Earth moon globe",
+                                               u : "Unspecified",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Color",
+                               values: {       a : "One color",
+                                               c : "Multicolored"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Physical medium",
+                               values: {       a : "Paper",
+                                               b : "Wood",
+                                               c : "Stone",
+                                               d : "Metal",
+                                               e : "Synthetics",
+                                               f : "Skins",
+                                               g : "Textile",
+                                               p : "Plaster",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Type of reproduction",
+                               values: {       f : "Facsimile",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       }
+               }
+       },
+       a : {
+               label     : "Map",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       d : "Atlas",
+                                               g : "Diagram",
+                                               j : "Map",
+                                               k : "Profile",
+                                               q : "Model",
+                                               r : "Remote-sensing image",
+                                               s : "Section",
+                                               u : "Unspecified",
+                                               y : "View",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Color",
+                               values: {       a : "One color",
+                                               c : "Multicolored"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Physical medium",
+                               values: {       a : "Paper",
+                                               b : "Wood",
+                                               c : "Stone",
+                                               d : "Metal",
+                                               e : "Synthetics",
+                                               f : "Skins",
+                                               g : "Textile",
+                                               p : "Plaster",
+                                               q : "Flexible base photographic medium, positive",
+                                               r : "Flexible base photographic medium, negative",
+                                               s : "Non-flexible base photographic medium, positive",
+                                               t : "Non-flexible base photographic medium, negative",
+                                               u : "Unknown",
+                                               y : "Other photographic medium",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Type of reproduction",
+                               values: {       f : "Facsimile",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       g : {   start : 6,
+                               len   : 1,
+                               label : "Production/reproduction details",
+                               values: {       a : "Photocopy, blueline print",
+                                               b : "Photocopy",
+                                               c : "Pre-production",
+                                               d : "Film",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       h : {   start : 7,
+                               len   : 1,
+                               label : "Positive/negative",
+                               values: {       a : "Positive",
+                                               b : "Negative",
+                                               m : "Mixed",
+                                               n : "Not applicable"
+                               }
+                       }
+               }
+       },
+       h : {
+               label     : "Microform",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       a : "Aperture card",
+                                               b : "Microfilm cartridge",
+                                               c : "Microfilm cassette",
+                                               d : "Microfilm reel",
+                                               e : "Microfiche",
+                                               f : "Microfiche cassette",
+                                               g : "Microopaque",
+                                               u : "Unspecified",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Positive/negative",
+                               values: {       a : "Positive",
+                                               b : "Negative",
+                                               m : "Mixed",
+                                               u : "Unknown"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Dimensions",
+                               values: {       a : "8 mm.",
+                                               e : "16 mm.",
+                                               f : "35 mm.",
+                                               g : "70mm.",
+                                               h : "105 mm.",
+                                               l : "3 x 5 in. (8 x 13 cm.)",
+                                               m : "4 x 6 in. (11 x 15 cm.)",
+                                               o : "6 x 9 in. (16 x 23 cm.)",
+                                               p : "3 1/4 x 7 3/8 in. (9 x 19 cm.)",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 4,
+                               label : "Reduction ratio range/Reduction ratio",
+                               values: {       a : "Low (1-16x)",
+                                               b : "Normal (16-30x)",
+                                               c : "High (31-60x)",
+                                               d : "Very high (61-90x)",
+                                               e : "Ultra (90x-)",
+                                               u : "Unknown",
+                                               v : "Reduction ratio varies"
+                               }
+                       },
+                       g : {   start : 9,
+                               len   : 1,
+                               label : "Color",
+                               values: {       b : "Black-and-white",
+                                               c : "Multicolored",
+                                               m : "Mixed",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       h : {   start : 10,
+                               len   : 1,
+                               label : "Emulsion on film",
+                               values: {       a : "Silver halide",
+                                               b : "Diazo",
+                                               c : "Vesicular",
+                                               m : "Mixed",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       i : {   start : 11,
+                               len   : 1,
+                               label : "Quality assurance target(s)",
+                               values: {       a : "1st gen. master",
+                                               b : "Printing master",
+                                               c : "Service copy",
+                                               m : "Mixed generation",
+                                               u : "Unknown"
+                               }
+                       },
+                       j : {   start : 12,
+                               len   : 1,
+                               label : "Base of film",
+                               values: {       a : "Safety base, undetermined",
+                                               c : "Safety base, acetate undetermined",
+                                               d : "Safety base, diacetate",
+                                               l : "Nitrate base",
+                                               m : "Mixed base",
+                                               n : "Not applicable",
+                                               p : "Safety base, polyester",
+                                               r : "Safety base, mixed",
+                                               t : "Safety base, triacetate",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       }
+               }
+       },
+       m : {
+               label     : "Motion Picture",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       a : "Film cartridge",
+                                               f : "Film cassette",
+                                               r : "Film reel",
+                                               u : "Unspecified",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Color",
+                               values: {       b : "Black-and-white",
+                                               c : "Multicolored",
+                                               h : "Hand-colored",
+                                               m : "Mixed",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Motion picture presentation format",
+                               values: {       a : "Standard sound aperture, reduced frame",
+                                               b : "Nonanamorphic (wide-screen)",
+                                               c : "3D",
+                                               d : "Anamorphic (wide-screen)",
+                                               e : "Other-wide screen format",
+                                               f : "Standard. silent aperture, full frame",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Sound on medium or separate",
+                               values: {       a : "Sound on medium",
+                                               b : "Sound separate from medium",
+                                               u : "Unknown"
+                               }
+                       },
+                       g : {   start : 6,
+                               len   : 1,
+                               label : "Medium for sound",
+                               values: {       a : "Optical sound track on motion picture film",
+                                               b : "Magnetic sound track on motion picture film",
+                                               c : "Magnetic audio tape in cartridge",
+                                               d : "Sound disc",
+                                               e : "Magnetic audio tape on reel",
+                                               f : "Magnetic audio tape in cassette",
+                                               g : "Optical and magnetic sound track on film",
+                                               h : "Videotape",
+                                               i : "Videodisc",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       h : {   start : 7,
+                               len   : 1,
+                               label : "Dimensions",
+                               values: {       a : "Standard 8 mm.",
+                                               b : "Super 8 mm./single 8 mm.",
+                                               c : "9.5 mm.",
+                                               d : "16 mm.",
+                                               e : "28 mm.",
+                                               f : "35 mm.",
+                                               g : "70 mm.",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       i : {   start : 8,
+                               len   : 1,
+                               label : "Configuration of playback channels",
+                               values: {       k : "Mixed",
+                                               m : "Monaural",
+                                               n : "Not applicable",
+                                               q : "Multichannel, surround or quadraphonic",
+                                               s : "Stereophonic",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       j : {   start : 9,
+                               len   : 1,
+                               label : "Production elements",
+                               values: {       a : "Work print",
+                                               b : "Trims",
+                                               c : "Outtakes",
+                                               d : "Rushes",
+                                               e : "Mixing tracks",
+                                               f : "Title bands/inter-title rolls",
+                                               g : "Production rolls",
+                                               n : "Not applicable",
+                                               z : "Other"
+                               }
+                       }
+               }
+       },
+       k : {
+               label     : "Non-projected Graphic",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       c : "Collage",
+                                               d : "Drawing",
+                                               e : "Painting",
+                                               f : "Photo-mechanical print",
+                                               g : "Photonegative",
+                                               h : "Photoprint",
+                                               i : "Picture",
+                                               j : "Print",
+                                               l : "Technical drawing",
+                                               n : "Chart",
+                                               o : "Flash/activity card",
+                                               u : "Unspecified",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Color",
+                               values: {       a : "One color",
+                                               b : "Black-and-white",
+                                               c : "Multicolored",
+                                               h : "Hand-colored",
+                                               m : "Mixed",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Primary support material",
+                               values: {       a : "Canvas",
+                                               b : "Bristol board",
+                                               c : "Cardboard/illustration board",
+                                               d : "Glass",
+                                               e : "Synthetics",
+                                               f : "Skins",
+                                               g : "Textile",
+                                               h : "Metal",
+                                               m : "Mixed collection",
+                                               o : "Paper",
+                                               p : "Plaster",
+                                               q : "Hardboard",
+                                               r : "Porcelain",
+                                               s : "Stone",
+                                               t : "Wood",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Secondary support material",
+                               values: {       a : "Canvas",
+                                               b : "Bristol board",
+                                               c : "Cardboard/illustration board",
+                                               d : "Glass",
+                                               e : "Synthetics",
+                                               f : "Skins",
+                                               g : "Textile",
+                                               h : "Metal",
+                                               m : "Mixed collection",
+                                               o : "Paper",
+                                               p : "Plaster",
+                                               q : "Hardboard",
+                                               r : "Porcelain",
+                                               s : "Stone",
+                                               t : "Wood",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       }
+               }
+       },
+       g : {
+               label     : "Projected Graphic",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       c : "Film cartridge",
+                                               d : "Filmstrip",
+                                               f : "Film filmstrip type",
+                                               o : "Filmstrip roll",
+                                               s : "Slide",
+                                               t : "Transparency",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Color",
+                               values: {       b : "Black-and-white",
+                                               c : "Multicolored",
+                                               h : "Hand-colored",
+                                               m : "Mixed",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Base of emulsion",
+                               values: {       d : "Glass",
+                                               e : "Synthetics",
+                                               j : "Safety film",
+                                               k : "Film base, other than safety film",
+                                               m : "Mixed collection",
+                                               o : "Paper",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Sound on medium or separate",
+                               values: {       a : "Sound on medium",
+                                               b : "Sound separate from medium",
+                                               u : "Unknown"
+                               }
+                       },
+                       g : {   start : 6,
+                               len   : 1,
+                               label : "Medium for sound",
+                               values: {       a : "Optical sound track on motion picture film",
+                                               b : "Magnetic sound track on motion picture film",
+                                               c : "Magnetic audio tape in cartridge",
+                                               d : "Sound disc",
+                                               e : "Magnetic audio tape on reel",
+                                               f : "Magnetic audio tape in cassette",
+                                               g : "Optical and magnetic sound track on film",
+                                               h : "Videotape",
+                                               i : "Videodisc",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       h : {   start : 7,
+                               len   : 1,
+                               label : "Dimensions",
+                               values: {       a : "Standard 8 mm.",
+                                               b : "Super 8 mm./single 8 mm.",
+                                               c : "9.5 mm.",
+                                               d : "16 mm.",
+                                               e : "28 mm.",
+                                               f : "35 mm.",
+                                               g : "70 mm.",
+                                               j : "2 x 2 in. (5 x 5 cm.)",
+                                               k : "2 1/4 x 2 1/4 in. (6 x 6 cm.)",
+                                               s : "4 x 5 in. (10 x 13 cm.)",
+                                               t : "5 x 7 in. (13 x 18 cm.)",
+                                               v : "8 x 10 in. (21 x 26 cm.)",
+                                               w : "9 x 9 in. (23 x 23 cm.)",
+                                               x : "10 x 10 in. (26 x 26 cm.)",
+                                               y : "7 x 7 in. (18 x 18 cm.)",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       i : {   start : 8,
+                               len   : 1,
+                               label : "Secondary support material",
+                               values: {       c : "Cardboard",
+                                               d : "Glass",
+                                               e : "Synthetics",
+                                               h : "metal",
+                                               j : "Metal and glass",
+                                               k : "Synthetics and glass",
+                                               m : "Mixed collection",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       }
+               }
+       },
+       r : {
+               label     : "Remote-sensing Image",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: { u : "Unspecified" }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Altitude of sensor",
+                               values: {       a : "Surface",
+                                               b : "Airborne",
+                                               c : "Spaceborne",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Attitude of sensor",
+                               values: {       a : "Low oblique",
+                                               b : "High oblique",
+                                               c : "Vertical",
+                                               n : "Not applicable",
+                                               u : "Unknown"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Cloud cover",
+                               values: {       0 : "0-09%",
+                                               1 : "10-19%",
+                                               2 : "20-29%",
+                                               3 : "30-39%",
+                                               4 : "40-49%",
+                                               5 : "50-59%",
+                                               6 : "60-69%",
+                                               7 : "70-79%",
+                                               8 : "80-89%",
+                                               9 : "90-100%",
+                                               n : "Not applicable",
+                                               u : "Unknown"
+                               }
+                       },
+                       g : {   start : 6,
+                               len   : 1,
+                               label : "Platform construction type",
+                               values: {       a : "Balloon",
+                                               b : "Aircraft-low altitude",
+                                               c : "Aircraft-medium altitude",
+                                               d : "Aircraft-high altitude",
+                                               e : "Manned spacecraft",
+                                               f : "Unmanned spacecraft",
+                                               g : "Land-based remote-sensing device",
+                                               h : "Water surface-based remote-sensing device",
+                                               i : "Submersible remote-sensing device",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       h : {   start : 7,
+                               len   : 1,
+                               label : "Platform use category",
+                               values: {       a : "Meteorological",
+                                               b : "Surface observing",
+                                               c : "Space observing",
+                                               m : "Mixed uses",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       i : {   start : 8,
+                               len   : 1,
+                               label : "Sensor type",
+                               values: {       a : "Active",
+                                               b : "Passive",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       j : {   start : 9,
+                               len   : 2,
+                               label : "Data type",
+                               values: {       nn : "Not applicable",
+                                               uu : "Unknown",
+                                               zz : "Other",
+                                               aa : "Visible light",
+                                               da : "Near infrared",
+                                               db : "Middle infrared",
+                                               dc : "Far infrared",
+                                               dd : "Thermal infrared",
+                                               de : "Shortwave infrared (SWIR)",
+                                               df : "Reflective infrared",
+                                               dv : "Combinations",
+                                               dz : "Other infrared data",
+                                               ga : "Sidelooking airborne radar (SLAR)",
+                                               gb : "Synthetic aperture radar (SAR-single frequency)",
+                                               gc : "SAR-multi-frequency (multichannel)",
+                                               gd : "SAR-like polarization",
+                                               ge : "SAR-cross polarization",
+                                               gf : "Infometric SAR",
+                                               gg : "Polarmetric SAR",
+                                               gu : "Passive microwave mapping",
+                                               gz : "Other microwave data",
+                                               ja : "Far ultraviolet",
+                                               jb : "Middle ultraviolet",
+                                               jc : "Near ultraviolet",
+                                               jv : "Ultraviolet combinations",
+                                               jz : "Other ultraviolet data",
+                                               ma : "Multi-spectral, multidata",
+                                               mb : "Multi-temporal",
+                                               mm : "Combination of various data types",
+                                               pa : "Sonar-water depth",
+                                               pb : "Sonar-bottom topography images, sidescan",
+                                               pc : "Sonar-bottom topography, near-surface",
+                                               pd : "Sonar-bottom topography, near-bottom",
+                                               pe : "Seismic surveys",
+                                               pz : "Other acoustical data",
+                                               ra : "Gravity anomales (general)",
+                                               rb : "Free-air",
+                                               rc : "Bouger",
+                                               rd : "Isostatic",
+                                               sa : "Magnetic field",
+                                               ta : "Radiometric surveys"
+                               }
+                       }
+               }
+       },
+       s : {
+               label     : "Sound Recording",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       d : "Sound disc",
+                                               e : "Cylinder",
+                                               g : "Sound cartridge",
+                                               i : "Sound-track film",
+                                               q : "Roll",
+                                               s : "Sound cassette",
+                                               t : "Sound-tape reel",
+                                               u : "Unspecified",
+                                               w : "Wire recording",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Speed",
+                               values: {       a : "16 rpm",
+                                               b : "33 1/3 rpm",
+                                               c : "45 rpm",
+                                               d : "78 rpm",
+                                               e : "8 rpm",
+                                               f : "1.4 mps",
+                                               h : "120 rpm",
+                                               i : "160 rpm",
+                                               k : "15/16 ips",
+                                               l : "1 7/8 ips",
+                                               m : "3 3/4 ips",
+                                               o : "7 1/2 ips",
+                                               p : "15 ips",
+                                               r : "30 ips",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Configuration of playback channels",
+                               values: {       m : "Monaural",
+                                               q : "Quadraphonic",
+                                               s : "Stereophonic",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Groove width or pitch",
+                               values: {       m : "Microgroove/fine",
+                                               n : "Not applicable",
+                                               s : "Coarse/standard",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       g : {   start : 6,
+                               len   : 1,
+                               label : "Dimensions",
+                               values: {       a : "3 in.",
+                                               b : "5 in.",
+                                               c : "7 in.",
+                                               d : "10 in.",
+                                               e : "12 in.",
+                                               f : "16 in.",
+                                               g : "4 3/4 in. (12 cm.)",
+                                               j : "3 7/8 x 2 1/2 in.",
+                                               o : "5 1/4 x 3 7/8 in.",
+                                               s : "2 3/4 x 4 in.",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       h : {   start : 7,
+                               len   : 1,
+                               label : "Tape width",
+                               values: {       l : "1/8 in.",
+                                               m : "1/4in.",
+                                               n : "Not applicable",
+                                               o : "1/2 in.",
+                                               p : "1 in.",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       i : {   start : 8,
+                               len   : 1,
+                               label : "Tape configuration ",
+                               values: {       a : "Full (1) track",
+                                               b : "Half (2) track",
+                                               c : "Quarter (4) track",
+                                               d : "8 track",
+                                               e : "12 track",
+                                               f : "16 track",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       m : {   start : 12,
+                               len   : 1,
+                               label : "Special playback",
+                               values: {       a : "NAB standard",
+                                               b : "CCIR standard",
+                                               c : "Dolby-B encoded, standard Dolby",
+                                               d : "dbx encoded",
+                                               e : "Digital recording",
+                                               f : "Dolby-A encoded",
+                                               g : "Dolby-C encoded",
+                                               h : "CX encoded",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       n : {   start : 13,
+                               len   : 1,
+                               label : "Capture and storage",
+                               values: {       a : "Acoustical capture, direct storage",
+                                               b : "Direct storage, not acoustical",
+                                               d : "Digital storage",
+                                               e : "Analog electrical storage",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       }
+               }
+       },
+       f : {
+               label     : "Tactile Material",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       a : "Moon",
+                                               b : "Braille",
+                                               c : "Combination",
+                                               d : "Tactile, with no writing system",
+                                               u : "Unspecified",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 2,
+                               label : "Class of braille writing",
+                               values: {       a : "Literary braille",
+                                               b : "Format code braille",
+                                               c : "Mathematics and scientific braille",
+                                               d : "Computer braille",
+                                               e : "Music braille",
+                                               m : "Multiple braille types",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Level of contraction",
+                               values: {       a : "Uncontracted",
+                                               b : "Contracted",
+                                               m : "Combination",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 6,
+                               len   : 3,
+                               label : "Braille music format",
+                               values: {       a : "Bar over bar",
+                                               b : "Bar by bar",
+                                               c : "Line over line",
+                                               d : "Paragraph",
+                                               e : "Single line",
+                                               f : "Section by section",
+                                               g : "Line by line",
+                                               h : "Open score",
+                                               i : "Spanner short form scoring",
+                                               j : "Short form scoring",
+                                               k : "Outline",
+                                               l : "Vertical score",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       g : {   start : 9,
+                               len   : 1,
+                               label : "Special physical characteristics",
+                               values: {       a : "Print/braille",
+                                               b : "Jumbo or enlarged braille",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       }
+               }
+       },
+       v : {
+               label     : "Videorecording",
+               subfields : {
+                       b : {   start : 1,
+                               len   : 1,
+                               label : "SMD",
+                               values: {       c : "Videocartridge",
+                                               d : "Videodisc",
+                                               f : "Videocassette",
+                                               r : "Videoreel",
+                                               u : "Unspecified",
+                                               z : "Other"
+                               }
+                       },
+                       d : {   start : 3,
+                               len   : 1,
+                               label : "Color",
+                               values: {       b : "Black-and-white",
+                                               c : "Multicolored",
+                                               m : "Mixed",
+                                               n : "Not applicable",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       e : {   start : 4,
+                               len   : 1,
+                               label : "Videorecording format",
+                               values: {       a : "Beta",
+                                               b : "VHS",
+                                               c : "U-matic",
+                                               d : "EIAJ",
+                                               e : "Type C",
+                                               f : "Quadruplex",
+                                               g : "Laserdisc",
+                                               h : "CED",
+                                               i : "Betacam",
+                                               j : "Betacam SP",
+                                               k : "Super-VHS",
+                                               m : "M-II",
+                                               o : "D-2",
+                                               p : "8 mm.",
+                                               q : "Hi-8 mm.",
+                                               u : "Unknown",
+                                               v : "DVD",
+                                               z : "Other"
+                               }
+                       },
+                       f : {   start : 5,
+                               len   : 1,
+                               label : "Sound on medium or separate",
+                               values: {       a : "Sound on medium",
+                                               b : "Sound separate from medium",
+                                               u : "Unknown"
+                               }
+                       },
+                       g : {   start : 6,
+                               len   : 1,
+                               label : "Medium for sound",
+                               values: {       a : "Optical sound track on motion picture film",
+                                               b : "Magnetic sound track on motion picture film",
+                                               c : "Magnetic audio tape in cartridge",
+                                               d : "Sound disc",
+                                               e : "Magnetic audio tape on reel",
+                                               f : "Magnetic audio tape in cassette",
+                                               g : "Optical and magnetic sound track on motion picture film",
+                                               h : "Videotape",
+                                               i : "Videodisc",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       h : {   start : 7,
+                               len   : 1,
+                               label : "Dimensions",
+                               values: {       a : "8 mm.",
+                                               m : "1/4 in.",
+                                               o : "1/2 in.",
+                                               p : "1 in.",
+                                               q : "2 in.",
+                                               r : "3/4 in.",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       },
+                       i : {   start : 8,
+                               len   : 1,
+                               label : "Configuration of playback channel",
+                               values: {       k : "Mixed",
+                                               m : "Monaural",
+                                               n : "Not applicable",
+                                               q : "Multichannel, surround or quadraphonic",
+                                               s : "Stereophonic",
+                                               u : "Unknown",
+                                               z : "Other"
+                               }
+                       }
+               }
+       }
+    };
+    
+    MARC.Record.prototype.recordType = function () {
+    
+       var _t = this.leader.substr(MARC.Record._ff_pos.Type.ldr.BKS.start, MARC.Record._ff_pos.Type.ldr.BKS.len);
+       var _b = this.leader.substr(MARC.Record._ff_pos.BLvl.ldr.BKS.start, MARC.Record._ff_pos.BLvl.ldr.BKS.len);
+    
+       for (var t in MARC.Record._recType) {
+               if (_t.match(MARC.Record._recType[t].Type) && _b.match(MARC.Record._recType[t].BLvl)) {
+                       return t;
+               }
+       }
+        return 'BKS'; // default
+    }
+    
+    MARC.Record.prototype.videorecordingFormatName = function () {
+       var _7 = this.field('007').data;
+    
+       if (_7 && _7.match(/^v/)) {
+               var _v_e = _7.substr(
+                       MARC.Record._physical_characteristics.v.subfields.e.start,
+                       MARC.Record._physical_characteristics.v.subfields.e.len
+               );
+    
+               return MARC.Record._physical_characteristics.v.subfields.e.values[ _v_e ];
+       }
+    
+       return null;
+    }
+    
+    MARC.Record.prototype.videorecordingFormatCode = function () {
+       var _7 = this.field('007').data;
+    
+       if (_7 && _7.match(/^v/)) {
+               return _7.substr(
+                       MARC.Record._physical_characteristics.v.subfields.e.start,
+                       MARC.Record._physical_characteristics.v.subfields.e.len
+               );
+       }
+    
+       return null;
+    }
+    
+    MARC.Record.prototype.extractFixedField = function (field, dflt) {
+       if (!MARC.Record._ff_pos[field]) return null;
+    
+       var _l = this.leader;
+       var _8 = this.field('008').data;
+       var _6 = this.field('006').data;
+    
+       var rtype = this.recordType();
+    
+       var val;
+    
+       if (MARC.Record._ff_pos[field].ldr && _l) {
+               if (MARC.Record._ff_pos[field].ldr[rtype]) {
+                       val = _l.substr(
+                               MARC.Record._ff_pos[field].ldr[rtype].start,
+                               MARC.Record._ff_pos[field].ldr[rtype].len
+                       );
+               }
+       } else if (MARC.Record._ff_pos[field]._8 && _8) {
+               if (MARC.Record._ff_pos[field]._8[rtype]) {
+                       val = _8.substr(
+                               MARC.Record._ff_pos[field]._8[rtype].start,
+                               MARC.Record._ff_pos[field]._8[rtype].len
+                       );
+               }
+       }
+    
+       if (!val && MARC.Record._ff_pos[field]._6 && _6) {
+               if (MARC.Record._ff_pos[field]._6[rtype]) {
+                       val = _6.substr(
+                               MARC.Record._ff_pos[field]._6[rtype].start,
+                               MARC.Record._ff_pos[field]._6[rtype].len
+                       );
+               }
+       }
+
+        if (!val && dflt) {
+            val = '';
+            var d;
+            var p;
+            if (MARC.Record._ff_pos[field].ldr && MARC.Record._ff_pos[field].ldr[rtype]) {
+                d = MARC.Record._ff_pos[field].ldr[rtype].def;
+                p = 'ldr';
+            }
+
+            if (MARC.Record._ff_pos[field]._8 && MARC.Record._ff_pos[field]._8[rtype]) {
+                d = MARC.Record._ff_pos[field]._8[rtype].def;
+                p = '_8';
+            }
+
+            if (!val && MARC.Record._ff_pos[field]._6 && MARC.Record._ff_pos[field]._6[rtype]) {
+                d = MARC.Record._ff_pos[field]._6[rtype].def;
+                p = '_6';
+            }
+
+            if (p) {
+                for (var j = 0; j < MARC.Record._ff_pos[field][p][rtype].len; j++) {
+                    val += d;
+                }
+            } else {
+                val = null;
+            }
+        }
+
+       return val;
+    }
+
+    MARC.Record.prototype.setFixedField = function (field, value) {
+       if (!MARC.Record._ff_pos[field]) return null;
+    
+       var _l = this.leader;
+       var _8 = this.field('008').data;
+       var _6 = this.field('006').data;
+    
+       var rtype = this.recordType();
+    
+       var val;
+    
+       if (MARC.Record._ff_pos[field].ldr && _l) {
+               if (MARC.Record._ff_pos[field].ldr[rtype]) { // It's in the leader
+                val = value.substr(0, MARC.Record._ff_pos[field].ldr[rtype].len);
+                this.leader =
+                    _l.substring(0, MARC.Record._ff_pos[field].ldr[rtype].start) +
+                    val +
+                    _l.substring(
+                        MARC.Record._ff_pos[field].ldr[rtype].start
+                        + MARC.Record._ff_pos[field].ldr[rtype].len
+                    );
+               }
+       } else if (MARC.Record._ff_pos[field]._8 && _8) {
+               if (MARC.Record._ff_pos[field]._8[rtype]) { // Nope, it's in the 008
+                val = value.substr(0, MARC.Record._ff_pos[field]._8[rtype].len);
+                this.field('008').update(
+                    _8.substring(0, MARC.Record._ff_pos[field]._8[rtype].start) +
+                    val +
+                    _8.substring(
+                        MARC.Record._ff_pos[field]._8[rtype].start
+                        + MARC.Record._ff_pos[field]._8[rtype].len
+                    )
+                );
+               }
+       }
+    
+       if (!val && MARC.Record._ff_pos[field]._6 && _6) {
+               if (MARC.Record._ff_pos[field]._6[rtype]) { // ok, maybe the 006?
+                val = value.substr(0, MARC.Record._ff_pos[field]._6[rtype].len);
+                this.field('006').update(
+                    _6.substring(0, MARC.Record._ff_pos[field]._6[rtype].start) +
+                    val +
+                    _6.substring(
+                        MARC.Record._ff_pos[field]._6[rtype].start
+                        + MARC.Record._ff_pos[field]._6[rtype].len