Merge branch 'master' of git+ssh://yeti.esilibrary.com/home/evergreen/evergreen-equin...
authorberick <berick@esilibrary.com>
Fri, 11 Mar 2011 21:52:30 +0000 (16:52 -0500)
committerberick <berick@esilibrary.com>
Fri, 11 Mar 2011 21:52:30 +0000 (16:52 -0500)
59 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/extras/Makefile.install
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/CreditCard.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/ResolverResolver.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.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/030.schema.metabib.sql
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/090.schema.action.sql
Open-ILS/src/sql/Pg/100.circ_matrix.sql
Open-ILS/src/sql/Pg/110.hold_matrix.sql
Open-ILS/src/sql/Pg/800.fkeys.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/0494.data.search_indexes.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0495.schema.svf-phase-one.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0496.data.search_indexes.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0497.patron_registration.sql [new file with mode: 0644]
Open-ILS/src/support-scripts/marc_export
Open-ILS/web/css/skin/default/register.css
Open-ILS/web/js/dojo/openils/actor/nls/register.js
Open-ILS/web/js/dojo/openils/booking/nls/pickup_and_return.js
Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
Open-ILS/web/js/ui/default/acq/financial/claim_eligible.js
Open-ILS/web/js/ui/default/actor/user/register.js
Open-ILS/web/js/ui/default/booking/populator.js
Open-ILS/web/opac/common/js/config.js
Open-ILS/web/opac/locale/en-US/conify.dtd
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/web/opac/skin/craftsman/xml/advanced/advanced_global.xml
Open-ILS/web/opac/skin/craftsman/xml/result/filtersort.xml
Open-ILS/web/opac/skin/default/js/adv_global.js
Open-ILS/web/opac/skin/default/js/myopac.js
Open-ILS/web/opac/skin/default/xml/advanced/advanced_global.xml
Open-ILS/web/opac/skin/default/xml/result/filtersort.xml
Open-ILS/web/templates/default/actor/user/register.tt2
Open-ILS/web/templates/default/actor/user/register_table.tt2
Open-ILS/web/templates/default/conify/global/config/coded_value_map.tt2 [new file with mode: 0644]
Open-ILS/web/templates/default/conify/global/config/record_attr_definition.tt2 [new file with mode: 0644]
Open-ILS/xul/staff_client/Makefile.am
Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js
Open-ILS/xul/staff_client/chrome/content/main/constants.js
Open-ILS/xul/staff_client/chrome/content/main/menu.js
Open-ILS/xul/staff_client/server/admin/org_unit_settings.js
Open-ILS/xul/staff_client/server/admin/org_unit_settings.xhtml
Open-ILS/xul/staff_client/server/cat/copy_editor.js
Open-ILS/xul/staff_client/server/locale/en-US/common.properties
Open-ILS/xul/staff_client/server/main/ws_info.xul
Open-ILS/xul/staff_client/server/patron/bill2.js
README
build/i18n/Makefile

index 60e38b8..8c39296 100644 (file)
@@ -571,6 +571,96 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
         </permacrud>
        </class>
 
+       <class id="crad" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::record_attr_definition" oils_persist:tablename="config.record_attr_definition" reporter:label="SVF Record Attribute Defintion" oils_persist:field_safe="true">
+               <fields oils_persist:primary="name">
+                       <field reporter:label="Name" name="name" reporter:datatype="id" reporter:selector="label"  oils_obj:required="true"/>
+                       <field reporter:label="Label" name="label" reporter:datatype="text"  oils_obj:required="true"/>
+                       <field reporter:label="Description" name="description" reporter:datatype="text" />
+                       <field reporter:label="Filter?" name="filter" reporter:datatype="bool"/>
+                       <field reporter:label="Sorter?" name="sorter" reporter:datatype="bool"/>
+                       <field reporter:label="MARC Tag" name="tag" reporter:datatype="text"/>
+                       <field reporter:label="MARC Subfields" name="sf_list" reporter:datatype="text"/>
+                       <field reporter:label="Joiner" name="joiner" reporter:datatype="text"/>
+                       <field reporter:label="XPath" name="xpath" reporter:datatype="text"/>
+                       <field reporter:label="Format" name="format" reporter:datatype="link"/>
+                       <field reporter:label="Starting Position" name="start_pos" reporter:datatype="int"/>
+                       <field reporter:label="String Length" name="string_len" reporter:datatype="int"/>
+                       <field reporter:label="Fixed Field" name="fixed_field" reporter:datatype="text"/>
+                       <field reporter:label="Physical Characteristic" name="phys_char_sf" reporter:datatype="text"/>
+                       <field reporter:label="Normalizers" name="normalizers" reporter:datatype="link" oils_persist:virtual="true"/>
+               </fields>
+               <links>
+                       <link field="format" reltype="has_a" key="name" map="" class="cxt"/>
+                       <link field="normalizers" reltype="has_many" key="name" map="" class="crainm"/>
+               </links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_SVF" global_required="true"/>
+                <retrieve/>
+                <update permission="ADMIN_SVF" global_required="true"/>
+                <delete permission="ADMIN_SVF" global_required="true"/>
+            </actions>
+        </permacrud>
+       </class>
+
+       <class id="mra" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="metabib::record_attr" oils_persist:tablename="metabib.record_attr" reporter:label="SVF Record Attribute" oils_persist:field_safe="true">
+               <fields oils_persist:primary="id">
+                       <field reporter:label="Record ID" name="id" reporter:datatype="id" oils_obj:required="true"/>
+                       <field reporter:label="Attributes" name="attrs" reporter:datatype="text"  oils_obj:required="true"/>
+               </fields>
+               <links>
+                       <link field="id" reltype="has_a" key="id" map="" class="bre"/>
+               </links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <retrieve/>
+            </actions>
+        </permacrud>
+       </class>
+
+       <class id="crainm" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::record_attr_index_norm_map" oils_persist:tablename="config.record_attr_index_norm_map" reporter:label="SVF Record Attribute to Indexing Normalizer Map" oils_persist:field_safe="true">
+               <fields oils_persist:primary="id" oils_persist:sequence="config.record_attr_index_norm_map_id_seq">
+                       <field reporter:label="ID" name="id" reporter:datatype="id"/>
+                       <field reporter:label="SVF Attribute" name="attr" reporter:datatype="link"/>
+                       <field reporter:label="Normalizer" name="norm" reporter:datatype="link"/>
+                       <field reporter:label="Parameters (JSON Array)" name="params" reporter:datatype="text"/>
+                       <field reporter:label="Order of Application" name="pos" reporter:datatype="int"/>
+               </fields>
+               <links>
+                       <link field="attr" reltype="has_a" key="name" map="" class="crad"/>
+                       <link field="norm" reltype="has_a" key="id" map="" class="cin"/>
+               </links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_INDEX_NORMALIZER" global_required="true"/>
+                <retrieve/>
+                <update permission="ADMIN_INDEX_NORMALIZER" global_required="true"/>
+                <delete permission="ADMIN_INDEX_NORMALIZER" global_required="true"/>
+            </actions>
+        </permacrud>
+       </class>
+
+       <class id="ccvm" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::coded_value_map" oils_persist:tablename="config.coded_value_map" reporter:label="SVF Record Attribute Coded Value Map" oils_persist:field_safe="true">
+               <fields oils_persist:primary="id" oils_persist:sequence="config.coded_value_map_id_seq">
+                       <field reporter:label="ID" name="id" reporter:datatype="id"  oils_obj:required="true"/>
+                       <field reporter:label="SVF Attribute" name="ctype" reporter:datatype="link"  oils_obj:required="true"/>
+                       <field reporter:label="Code" name="code" reporter:datatype="text"  oils_obj:required="true"/>
+                       <field reporter:label="Value" name="value" reporter:datatype="text"  oils_obj:required="true"/>
+                       <field reporter:label="Description" name="description" reporter:datatype="text"/>
+               </fields>
+               <links>
+                       <link field="ctype" reltype="has_a" key="name" map="" class="crad"/>
+               </links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                <create permission="ADMIN_CODED_VALUE" global_required="true"/>
+                <retrieve/>
+                <update permission="ADMIN_CODED_VALUE" global_required="true"/>
+                <delete permission="ADMIN_CODED_VALUE" global_required="true"/>
+            </actions>
+        </permacrud>
+       </class>
+
        <class id="cracct" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::remote_account" oils_persist:tablename="config.remote_account" reporter:label="Remote (3rd party) Account">
                <fields oils_persist:primary="id" oils_persist:sequence="config.remote_account_id_seq">
                        <field name="id"            reporter:datatype="id"   reporter:label="ID"/>
@@ -1875,6 +1965,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="Simple Record Extracts " name="simple_record" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Authority Links" name="authority_links" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Subscriptions" name="subscriptions" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="SVF Attributes" name="attrs" oils_persist:virtual="true" reporter:datatype="link"/>
                </fields>
                <links>
                        <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
@@ -1895,6 +1986,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <link field="full_record_entries" reltype="has_many" key="record" map="" class="mfr"/>
                        <link field="authority_links" reltype="has_many" key="bib" map="" class="abl"/>
                        <link field="subscriptions" reltype="has_many" key="record_entry" map="" class="ssub"/>
+                       <link field="attrs" reltype="might_have" key="id" map="" class="mra"/>
                </links>
         <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
             <actions>
index 80c47b2..fef69ef 100644 (file)
@@ -381,7 +381,7 @@ install_pgsql_client_debs_84:
        $(APT_TOOL) install $(PGSQL_CLIENT_DEBS_84)
 
 install_pgsql_server_debs_84:
-       LANG=C $(APT_TOOL) install $(PGSQL_SERVER_DEBS_84)
+       $(APT_TOOL) install $(PGSQL_SERVER_DEBS_84)
 
 install_pgsql_client_debs_83:
        $(APT_TOOL) install $(PGSQL_CLIENT_DEBS_83)
index 97e4771..0e46529 100644 (file)
@@ -180,6 +180,10 @@ __PACKAGE__->register_method(
 );
 __PACKAGE__->register_method(
     method   => "run_method",
+    api_name => "open-ils.circ.reservation.return.override"
+);
+__PACKAGE__->register_method(
+    method   => "run_method",
     api_name => "open-ils.circ.checkout.inspect",
     desc     => q/Returns the circ matrix test result and, on success, the rule set and matrix test object/
 );
index 647cc26..5704684 100644 (file)
@@ -155,7 +155,7 @@ sub process_payment {
             $argshash->{patron_id},
             {
                 flesh        => 1,
-                flesh_fields => { au => ["mailing_address"] }
+                flesh_fields => { au => ["mailing_address", "card"] }
             }
         ]
     ) or return $e->event;
@@ -197,6 +197,9 @@ sub prepare_bop_content {
     $content{FirstName}    = $content{first_name};   # kludge mcugly for PP
     $content{LastName}     = $content{last_name};
 
+    # makes patron barcode accessible in CC payment records
+    my $bc = ($patron->card) ? $patron->card->barcode : '';
+    $content{description}  = "$bc " . ($content{description} || '');
 
     # Especially for the following fields, do we need to support different
     # mapping of fields for different payment processors, particularly ones
index dcf19cc..a88cdac 100644 (file)
@@ -323,11 +323,11 @@ sub make_payments {
         my $amount = $payment->amount;
         my $transid = $payment->xact;
         my $trans = $xacts{$transid};
+        # making payment with existing patron credit.
+        $credit -= $amount if $type eq 'credit_payment';
         if( (my $cred = ($trans->balance_owed - $amount)) <= 0 ) {
             # Any overpay on this transaction goes directly into patron
-            # credit making payment with existing patron credit.
-            $credit -= $amount if $type eq 'credit_payment';
-
+            # credit
             $cred = -$cred;
             $credit += $cred;
             my $circ = $e->retrieve_action_circulation($transid);
index 3463c1e..76a84ed 100644 (file)
@@ -123,7 +123,7 @@ sub resolve_holdings {
 
     # Big ugly SFX OpenURL request
     my $url_args = '?url_ver=Z39.88-2004&url_ctx_fmt=infofi/fmt:kev:mtx:ctx&'
-        . 'ctx_enc=UTF-8&ctx_ver=Z39.88-2004&rfr_id=info:sid/conifer&'
+        . 'ctx_enc=UTF-8&ctx_ver=Z39.88-2004&rfr_id=info:sid/evergreen&'
         . 'sfx.ignore_date_threshold=1&'
         . 'sfx.response_type=multi_obj_detailed_xml&__service_type=getFullTxt';
 
@@ -158,19 +158,27 @@ sub resolve_holdings {
 
     my @sfx_result;
     foreach my $target (@targets) {
+        my %full_txt;
+
+        # Ensure we have a name and especially URL to return
+        $full_txt{'name'} = $target->findvalue('./target_public_name') || next;
+        $full_txt{'url'} = $target->findvalue('.//target_url') || next;
+        $full_txt{'coverage'} = $target->findvalue('.//coverage_statement') || '';
+        $full_txt{'embargo'} = $target->findvalue('.//embargo_statement') || '';
+
         if ($format eq 'raw') {
             push @sfx_result, {
-                public_name => $target->findvalue('./target_public_name'),
-                target_url => $target->findvalue('.//target_url'),
-                target_coverage => $target->findvalue('.//coverage_statement'),
-                target_embargo => $target->findvalue('.//embargo_statement'),
+                public_name => $full_txt{'name'},
+                target_url => $full_txt{'url'},
+                target_coverage => $full_txt{'coverage'},
+                target_embargo => $full_txt{'embargo'},
             };
         } else {
             my $rhr = Fieldmapper::resolver::holdings_record->new;
-            $rhr->public_name($target->findvalue('./target_public_name'));
-            $rhr->target_url($target->findvalue('.//target_url'));
-            $rhr->target_coverage($target->findvalue('.//coverage_statement'));
-            $rhr->target_embargo($target->findvalue('.//embargo_statement'));
+            $rhr->public_name($full_txt{'name'});
+            $rhr->target_url($full_txt{'url'});
+            $rhr->target_coverage($full_txt{'coverage'});
+            $rhr->target_embargo($full_txt{'embargo'});
             push @sfx_result, $rhr;
         }
     }
@@ -271,7 +279,7 @@ sub delete_cached_holdings {
 }
 
 __PACKAGE__->register_method(
-    method    => 'delete_holdings_cache',
+    method    => 'delete_cached_holdings',
     api_name  => 'open-ils.resolver.delete_cached_holdings',
     api_level => 1,
     argc      => 3,
index 4406125..c2b17eb 100644 (file)
@@ -2266,46 +2266,49 @@ sub fetch_cn_by_info {
 
 __PACKAGE__->register_method(
     method   => 'bib_extras',
-    api_name => 'open-ils.search.biblio.lit_form_map.retrieve.all'
+    api_name => 'open-ils.search.biblio.lit_form_map.retrieve.all',
+    ctype => 'lit_form'
 );
 __PACKAGE__->register_method(
     method   => 'bib_extras',
-    api_name => 'open-ils.search.biblio.item_form_map.retrieve.all'
+    api_name => 'open-ils.search.biblio.item_form_map.retrieve.all',
+    ctype => 'item_form'
 );
 __PACKAGE__->register_method(
     method   => 'bib_extras',
-    api_name => 'open-ils.search.biblio.item_type_map.retrieve.all'
+    api_name => 'open-ils.search.biblio.item_type_map.retrieve.all',
+    ctype => 'item_type',
 );
 __PACKAGE__->register_method(
     method   => 'bib_extras',
-    api_name => 'open-ils.search.biblio.bib_level_map.retrieve.all'
+    api_name => 'open-ils.search.biblio.bib_level_map.retrieve.all',
+    ctype => 'bib_level'
 );
 __PACKAGE__->register_method(
     method   => 'bib_extras',
-    api_name => 'open-ils.search.biblio.audience_map.retrieve.all'
+    api_name => 'open-ils.search.biblio.audience_map.retrieve.all',
+    ctype => 'audience'
 );
 
 sub bib_extras {
        my $self = shift;
+    $logger->warn("deprecation warning: " .$self->api_name);
 
        my $e = new_editor();
 
-       return $e->retrieve_all_config_lit_form_map()
-               if( $self->api_name =~ /lit_form/ );
+    my $ctype = $self->{ctype};
+    my $ccvms = $e->search_config_coded_value_map({ctype => $ctype});
 
-       return $e->retrieve_all_config_item_form_map()
-               if( $self->api_name =~ /item_form_map/ );
-
-       return $e->retrieve_all_config_item_type_map()
-               if( $self->api_name =~ /item_type_map/ );
-
-       return $e->retrieve_all_config_bib_level_map()
-               if( $self->api_name =~ /bib_level_map/ );
-
-       return $e->retrieve_all_config_audience_map()
-               if( $self->api_name =~ /audience_map/ );
+    my @objs;
+    for my $ccvm (@$ccvms) {
+        my $obj = "Fieldmapper::config::${ctype}_map"->new;
+        $obj->value($ccvm->value);
+        $obj->code($ccvm->code);
+        $obj->description($ccvm->description) if $obj->can('description');
+        push(@objs, $obj);
+    }
 
-       return [];
+    return \@objs;
 }
 
 
index a27081e..a930931 100644 (file)
@@ -33,7 +33,6 @@ sub quote_phrase_value {
 
 sub init {
     my $class = shift;
-
 }
 
 sub default_preferred_language {
@@ -72,6 +71,24 @@ sub toSQL {
     return $self->parse_tree->toSQL;
 }
 
+sub dynamic_filters {
+    my $self = shift;
+    my $new = shift;
+
+    $self->custom_data->{dynamic_filters} ||= [];
+    push(@{$self->custom_data->{dynamic_filters}}, $new) if ($new);
+    return $self->custom_data->{dynamic_filters};
+}
+
+sub dynamic_sorters {
+    my $self = shift;
+    my $new = shift;
+
+    $self->custom_data->{dynamic_sorters} ||= [];
+    push(@{$self->custom_data->{dynamic_sorters}}, $new) if ($new);
+    return $self->custom_data->{dynamic_sorters};
+}
+
 sub facet_field_id_map {
     my $self = shift;
     my $map = shift;
@@ -237,7 +254,7 @@ sub initialize_relevance_bumps {
     return $self->relevance_bumps;
 }
 
-sub initialize_normalizers {
+sub initialize_query_normalizers {
     my $self = shift;
     my $tree = shift; # open-ils.cstore.direct.config.metabib_field_index_norm_map.search.atomic { "id" : { "!=" : null } }, { "flesh" : 1, "flesh_fields" : { "cmfinm" : ["norm"] }, "order_by" : [{ "class" : "cmfinm", "field" : "pos" }] }
 
@@ -247,6 +264,25 @@ sub initialize_normalizers {
     }
 }
 
+sub initialize_dynamic_filters {
+    my $self = shift;
+    my $list = shift; # open-ils.cstore.direct.config.record_attr_definition.search.atomic { "id" : { "!=" : null } }
+
+    for my $crad ( @$list ) {
+        __PACKAGE__->dynamic_filters( __PACKAGE__->add_search_filter( $crad->name ) ) if ($U->is_true($crad->filter));
+        __PACKAGE__->dynamic_sorters( $crad->name ) if ($U->is_true($crad->sorter));
+    }
+}
+
+sub initialize_filter_normalizers {
+    my $self = shift;
+    my $tree = shift; # open-ils.cstore.direct.config.record_attr_index_norm_map.search.atomic { "id" : { "!=" : null } }, { "flesh" : 1, "flesh_fields" : { "crainm" : ["norm"] }, "order_by" : [{ "class" : "crainm", "field" : "pos" }] }
+
+    for my $crainm ( @$tree ) {
+        __PACKAGE__->add_filter_normalizer( $crainm->name, $crainm->norm->func, OpenSRF::Utils::JSON->JSON2perl($crainm->params) );
+    }
+}
+
 our $_complete = 0;
 sub initialization_complete {
     return $_complete;
@@ -258,6 +294,18 @@ sub initialize {
 
     return $_complete if ($_complete);
 
+    # tsearch rank normalization adjustments. see http://www.postgresql.org/docs/9.0/interactive/textsearch-controls.html#TEXTSEARCH-RANKING for details
+    $self->custom_data->{rank_cd_weight_map} = {
+        CD_logDocumentLength    => 1,
+        CD_documentLength       => 2,
+        CD_meanHarmonic         => 4,
+        CD_uniqueWords          => 8,
+        CD_logUniqueWords       => 16,
+        CD_selfPlusOne          => 32
+    };
+
+    $self->add_search_modifier( $_ ) for (keys %{ $self->custom_data->{rank_cd_weight_map} });
+
     $self->initialize_search_field_id_map( $args{config_metabib_field} )
         if ($args{config_metabib_field});
 
@@ -267,14 +315,21 @@ sub initialize {
     $self->initialize_relevance_bumps( $args{search_relevance_adjustment} )
         if ($args{search_relevance_adjustment});
 
-    $self->initialize_normalizers( $args{config_metabib_field_index_norm_map} )
+    $self->initialize_query_normalizers( $args{config_metabib_field_index_norm_map} )
         if ($args{config_metabib_field_index_norm_map});
 
+    $self->initialize_dynamic_filters( $args{config_record_attr_definition} )
+        if ($args{config_record_attr_definition});
+
+    $self->initialize_filter_normalizers( $args{config_record_attr_index_norm_map} )
+        if ($args{config_record_attr_index_norm_map});
+
     $_complete = 1 if (
         $args{config_metabib_field_index_norm_map} &&
         $args{search_relevance_adjustment} &&
         $args{config_metabib_search_alias} &&
-        $args{config_metabib_field}
+        $args{config_metabib_field} &&
+        $args{config_record_attr_definition}
     );
 
     return $_complete;
@@ -348,43 +403,47 @@ sub TEST_SETUP {
 
 __PACKAGE__->default_search_class( 'keyword' );
 
-__PACKAGE__->add_search_filter( 'audience' );
-__PACKAGE__->add_search_filter( 'vr_format' );
+# will be retained simply for back-compat
 __PACKAGE__->add_search_filter( 'format' );
-__PACKAGE__->add_search_filter( 'item_type' );
-__PACKAGE__->add_search_filter( 'item_form' );
-__PACKAGE__->add_search_filter( 'lit_form' );
+
+# grumble grumble, special cases against date1 and date2
+__PACKAGE__->add_search_filter( 'before' );
+__PACKAGE__->add_search_filter( 'after' );
+__PACKAGE__->add_search_filter( 'between' );
+__PACKAGE__->add_search_filter( 'during' );
+
+# used by layers above this
+__PACKAGE__->add_search_filter( 'statuses' );
 __PACKAGE__->add_search_filter( 'locations' );
 __PACKAGE__->add_search_filter( 'site' );
 __PACKAGE__->add_search_filter( 'lasso' );
 __PACKAGE__->add_search_filter( 'my_lasso' );
 __PACKAGE__->add_search_filter( 'depth' );
-__PACKAGE__->add_search_filter( 'sort' );
 __PACKAGE__->add_search_filter( 'language' );
-__PACKAGE__->add_search_filter( 'preferred_language' );
-__PACKAGE__->add_search_filter( 'preferred_language_weight' );
-__PACKAGE__->add_search_filter( 'preferred_language_multiplier' );
-__PACKAGE__->add_search_filter( 'statuses' );
-__PACKAGE__->add_search_filter( 'bib_level' );
-__PACKAGE__->add_search_filter( 'before' );
-__PACKAGE__->add_search_filter( 'after' );
-__PACKAGE__->add_search_filter( 'between' );
-__PACKAGE__->add_search_filter( 'during' );
 __PACKAGE__->add_search_filter( 'offset' );
 __PACKAGE__->add_search_filter( 'limit' );
-__PACKAGE__->add_search_filter( 'core_limit' );
 __PACKAGE__->add_search_filter( 'check_limit' );
 __PACKAGE__->add_search_filter( 'skip_check' );
 __PACKAGE__->add_search_filter( 'superpage' );
 __PACKAGE__->add_search_filter( 'superpage_size' );
 __PACKAGE__->add_search_filter( 'estimation_strategy' );
-
 __PACKAGE__->add_search_modifier( 'available' );
+__PACKAGE__->add_search_modifier( 'staff' );
+
+# used internally, but generally not user-settable
+__PACKAGE__->add_search_filter( 'preferred_language' );
+__PACKAGE__->add_search_filter( 'preferred_language_weight' );
+__PACKAGE__->add_search_filter( 'preferred_language_multiplier' );
+__PACKAGE__->add_search_filter( 'core_limit' );
+
+# XXX Valid values to be supplied by SVF
+__PACKAGE__->add_search_filter( 'sort' );
+
+# modifies core query, not configurable
 __PACKAGE__->add_search_modifier( 'descending' );
 __PACKAGE__->add_search_modifier( 'ascending' );
 __PACKAGE__->add_search_modifier( 'metarecord' );
 __PACKAGE__->add_search_modifier( 'metabib' );
-__PACKAGE__->add_search_modifier( 'staff' );
 
 
 #-------------------------------
@@ -435,54 +494,53 @@ sub toSQL {
     if (($filters{preferred_language} || $self->QueryParser->default_preferred_language) && ($filters{preferred_language_multiplier} || $self->QueryParser->default_preferred_language_multiplier)) {
         my $pl = $self->QueryParser->quote_value( $filters{preferred_language} ? $filters{preferred_language} : $self->QueryParser->default_preferred_language );
         my $plw = $filters{preferred_language_multiplier} ? $filters{preferred_language_multiplier} : $self->QueryParser->default_preferred_language_multiplier;
-        $rel = "($rel * COALESCE( NULLIF( FIRST(mrd.item_lang) = $pl , FALSE )::INT * $plw, 1))";
+        $rel = "($rel * COALESCE( NULLIF( FIRST(mrd.attrs \@> hstore('item_lang', $pl)), FALSE )::INT * $plw, 1))";
     }
-    $rel .= '::NUMERIC';
+    $rel = "1.0/($rel)::NUMERIC";
 
-    for my $f ( qw/audience vr_format item_type item_form lit_form language bib_level/ ) {
+    my %dyn_filters = ( '' => [] ); # the "catch-all" key
+    for my $f ( @{ $self->QueryParser->dynamic_filters } ) {
         my $col = $f;
-        $col = 'item_lang' if ($f eq 'language');
-        $filters{$f} = '';
+        $col = 'item_lang' if ($f eq 'language'); #XXX filter aliases would address this ... booo ... later
+
         my ($filter) = $self->find_filter($f);
         if ($filter) {
-            $filters{$f} = "AND mrd.$col in (" . join(",",map { $self->QueryParser->quote_value($_) } @{$filter->args}) . ")";
+            my @fargs = @{$filter->args};
+
+            if (@fargs > 1) {
+                $dyn_filters{$f} = "( " .
+                    join(
+                        " OR ",
+                        map { "mrd.attrs \@> hstore('$col', " . $self->QueryParser->quote_value($_) . ")" } @fargs
+                    ) . 
+                    " )";
+            } else {
+                push(@{$dyn_filters{''}}, "hstore('$col', " . $self->QueryParser->quote_value($fargs[0]) . ")");
+            }
         }
     }
 
-    my $audience = $filters{audience};
-    my $vr_format = $filters{vr_format};
-    my $item_type = $filters{item_type};
-    my $item_form = $filters{item_form};
-    my $lit_form = $filters{lit_form};
-    my $language = $filters{language};
-    my $bib_level = $filters{bib_level};
+    my $combined_dyn_filters = '';
+    $combined_dyn_filters .= 'AND mrd.attrs @> (' . join(' || ', @{$dyn_filters{''}}) . ') ' if (@{$dyn_filters{''}});
+    delete($dyn_filters{''});
 
+    my @dyn_filter_list = values(%dyn_filters);
+    $combined_dyn_filters .= 'AND ' . join(' AND ', @dyn_filter_list) if (@dyn_filter_list);
+    
     my $rank = $rel;
 
     my $desc = 'ASC';
     $desc = 'DESC' if ($self->find_modifier('descending'));
 
-    if ($sort_filter eq 'rel') { # relevance ranking flips sort dir
-        if ($desc eq  'ASC') {
-            $desc = 'DESC';
-        } else {
-            $desc = 'ASC';
-        }
+    if (grep {$_ eq $sort_filter} @{$self->QueryParser->dynamic_sorters}) {
+        $rank = "FIRST(mrd.attrs->'$sort_filter')"
+    } elsif ($sort_filter eq 'create_date') {
+        $rank = "FIRST((SELECT create_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))";
+    } elsif ($sort_filter eq 'edit_date') {
+        $rank = "FIRST((SELECT edit_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))";
     } else {
-        if ($sort_filter eq 'title') {
-            $rank = "FIRST((SELECT frt.value FROM metabib.full_rec frt WHERE frt.record = m.source AND frt.tag = 'tnf' AND frt.subfield = 'a' LIMIT 1))";
-        } elsif ($sort_filter eq 'pubdate') {
-            $rank = "FIRST(mrd.date1)::NUMERIC";
-        } elsif ($sort_filter eq 'create_date') {
-            $rank = "FIRST((SELECT create_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))";
-        } elsif ($sort_filter eq 'edit_date') {
-            $rank = "FIRST((SELECT edit_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))";
-        } elsif ($sort_filter eq 'author') {
-            $rank = "FIRST((SELECT fra.value FROM metabib.full_rec fra WHERE fra.record = m.source AND fra.tag LIKE '1%' AND fra.subfield = 'a' ORDER BY fra.tag LIMIT 1))";
-        } else {
-            # default to rel ranking
-            $rank = $rel;
-        }
+        # default to rel ranking
+        $rank = $rel;
     }
 
     my $key = 'm.source';
@@ -494,25 +552,25 @@ sub toSQL {
     my ($between) = $self->find_filter('between');
 
     if ($before and @{$before->args} == 1) {
-        $before = "AND mrd.date1 <= " . $self->QueryParser->quote_value($before->args->[0]);
+        $before = "AND (mrd.attrs->'date1') <= " . $self->QueryParser->quote_value($before->args->[0]);
     } else {
         $before = '';
     }
 
     if ($after and @{$after->args} == 1) {
-        $after = "AND mrd.date1 >= " . $self->QueryParser->quote_value($after->args->[0]);
+        $after = "AND (mrd.attrs->'date1') >= " . $self->QueryParser->quote_value($after->args->[0]);
     } else {
         $after = '';
     }
 
     if ($during and @{$during->args} == 1) {
-        $during = "AND " . $self->QueryParser->quote_value($during->args->[0]) . " BETWEEN mrd.date1 AND mrd.date2";
+        $during = "AND " . $self->QueryParser->quote_value($during->args->[0]) . " BETWEEN (mrd.attrs->'date1') AND (mrd.attrs->'date2')";
     } else {
         $during = '';
     }
 
     if ($between and @{$between->args} == 2) {
-        $between = "AND mrd.date1 BETWEEN " . $self->QueryParser->quote_value($between->args->[0]) . " AND " . $self->QueryParser->quote_value($between->args->[1]);
+        $between = "AND (mrd.attrs->'date1') BETWEEN " . $self->QueryParser->quote_value($between->args->[0]) . " AND " . $self->QueryParser->quote_value($between->args->[1]);
     } else {
         $between = '';
     }
@@ -524,22 +582,16 @@ SELECT  $key AS id,
         ARRAY_ACCUM(DISTINCT m.source) AS records,
         $rel AS rel,
         $rank AS rank, 
-        FIRST(mrd.date1) AS tie_break
+        FIRST(mrd.attrs->'date1') AS tie_break
   FROM  metabib.metarecord_source_map m
-        JOIN metabib.rec_descriptor mrd ON (m.source = mrd.record)
+        JOIN metabib.record_attr mrd ON (m.source = mrd.id)
         $$flat_plan{from}
   WHERE 1=1
         $before
         $after
         $during
         $between
-        $audience
-        $vr_format
-        $item_type
-        $item_form
-        $lit_form
-        $language
-        $bib_level
+        $combined_dyn_filters
         AND $$flat_plan{where}
   GROUP BY 1
   ORDER BY 4 $desc NULLS LAST, 5 DESC NULLS LAST, 3 DESC
@@ -828,8 +880,16 @@ sub tsquery {
 
 sub rank {
     my $self = shift;
+
+    my $rank_norm_map = $self->plan->QueryParser->custom_data->{rank_cd_weight_map};
+    
+    my $cover_density = 0;
+    for my $norm ( keys %$rank_norm_map) {
+        $cover_density += $$rank_norm_map{$norm} if ($self->plan->find_modifier($norm));
+    }
+
     return $self->{rank} if ($self->{rank});
-    return $self->{rank} = 'rank(' . $self->table_alias . '.index_vector, ' . $self->table_alias . '.tsq)';
+    return $self->{rank} = 'rank_cd(' . $self->table_alias . '.index_vector, ' . $self->table_alias . ".tsq, $cover_density)";
 }
 
 
index 22362cf..212dd45 100644 (file)
@@ -10,8 +10,9 @@ use DateTime;
 use DateTime::Format::ISO8601;  
 use DateTime::Set;
 use DateTime::SpanSet;
-                                                
-                                                                                               
+
+use Encode;
+
 my $_dt_parser = DateTime::Format::ISO8601->new;    
 
 my $log = 'OpenSRF::Utils::Logger';
@@ -643,15 +644,15 @@ sub patron_search {
        # group 3 = barcode
 
        my $usr = join ' AND ', map { "LOWER(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
-       my @usrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
+       my @usrv = map { "^" . encode_utf8($$search{$_}{value}) } grep { ''.$$search{$_}{group} eq '0' } keys %$search;
 
        my $addr = join ' AND ', map { "LOWER(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
-       my @addrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
+       my @addrv = map { "^" . encode_utf8($$search{$_}{value}) } grep { ''.$$search{$_}{group} eq '1' } keys %$search;
 
-       my $pv = $$search{phone}{value};
-       my $iv = $$search{ident}{value};
-       my $nv = $$search{name}{value};
-       my $cv = $$search{card}{value};
+       my $pv = encode_utf8($$search{phone}{value});
+       my $iv = encode_utf8($$search{ident}{value});
+       my $nv = encode_utf8($$search{name}{value});
+       my $cv = encode_utf8($$search{card}{value});
 
        my $card = '';
        if ($cv) {
index c70b5cd..ede8cd0 100644 (file)
@@ -2784,11 +2784,11 @@ sub query_parser_fts {
     if (!$parser->initialization_complete) {
         my $cstore = OpenSRF::AppSession->create( 'open-ils.cstore' );
         $parser->initialize(
-            config_metabib_field_index_norm_map =>
+            config_record_attr_index_norm_map =>
                 $cstore->request(
-                    'open-ils.cstore.direct.config.metabib_field_index_norm_map.search.atomic',
+                    'open-ils.cstore.direct.config.record_attr_index_norm_map.search.atomic',
                     { id => { "!=" => undef } },
-                    { flesh => 1, flesh_fields => { cmfinm => [qw/norm/] }, order_by => [{ class => "cmfinm", field => "pos" }] }
+                    { flesh => 1, flesh_fields => { crainm => [qw/norm/] }, order_by => [{ class => "crainm", field => "pos" }] }
                 )->gather(1),
             search_relevance_adjustment         =>
                 $cstore->request(
@@ -2805,6 +2805,17 @@ sub query_parser_fts {
                     'open-ils.cstore.direct.config.metabib_search_alias.search.atomic',
                     { alias => { "!=" => undef } }
                 )->gather(1),
+            config_metabib_field_index_norm_map =>
+                $cstore->request(
+                    'open-ils.cstore.direct.config.metabib_field_index_norm_map.search.atomic',
+                    { id => { "!=" => undef } },
+                    { flesh => 1, flesh_fields => { cmfinm => [qw/norm/] }, order_by => [{ class => "cmfinm", field => "pos" }] }
+                )->gather(1),
+            config_record_attr_definition       =>
+                $cstore->request(
+                    'open-ils.cstore.direct.config.record_attr_definition.search.atomic',
+                    { name => { "!=" => undef } }
+                )->gather(1),
         );
 
         $cstore->disconnect;
@@ -3037,6 +3048,13 @@ sub query_parser_fts {
        }
        return undef;
 }
+__PACKAGE__->register_method(
+       api_name        => "open-ils.storage.query_parser_search",
+       method          => 'query_parser_fts',
+       api_level       => 1,
+       stream          => 1,
+       cachable        => 1,
+);
 
 sub query_parser_fts_wrapper {
        my $self = shift;
@@ -3051,11 +3069,11 @@ sub query_parser_fts_wrapper {
     if (!$parser->initialization_complete) {
         my $cstore = OpenSRF::AppSession->create( 'open-ils.cstore' );
         $parser->initialize(
-            config_metabib_field_index_norm_map =>
+            config_record_attr_index_norm_map =>
                 $cstore->request(
-                    'open-ils.cstore.direct.config.metabib_field_index_norm_map.search.atomic',
+                    'open-ils.cstore.direct.config.record_attr_index_norm_map.search.atomic',
                     { id => { "!=" => undef } },
-                    { flesh => 1, flesh_fields => { cmfinm => [qw/norm/] }, order_by => [{ class => "cmfinm", field => "pos" }] }
+                    { flesh => 1, flesh_fields => { crainm => [qw/norm/] }, order_by => [{ class => "crainm", field => "pos" }] }
                 )->gather(1),
             search_relevance_adjustment         =>
                 $cstore->request(
@@ -3072,6 +3090,17 @@ sub query_parser_fts_wrapper {
                     'open-ils.cstore.direct.config.metabib_search_alias.search.atomic',
                     { alias => { "!=" => undef } }
                 )->gather(1),
+            config_metabib_field_index_norm_map =>
+                $cstore->request(
+                    'open-ils.cstore.direct.config.metabib_field_index_norm_map.search.atomic',
+                    { id => { "!=" => undef } },
+                    { flesh => 1, flesh_fields => { cmfinm => [qw/norm/] }, order_by => [{ class => "cmfinm", field => "pos" }] }
+                )->gather(1),
+            config_record_attr_definition       =>
+                $cstore->request(
+                    'open-ils.cstore.direct.config.record_attr_definition.search.atomic',
+                    { name => { "!=" => undef } }
+                )->gather(1),
         );
 
         $cstore->disconnect;
index 6338384..7792b49 100644 (file)
@@ -207,6 +207,35 @@ sub query_normalizers {
     return $parser_config{$pkg}{normalizers};
 }
 
+sub add_filter_normalizer {
+    my $pkg = shift;
+    $pkg = ref($pkg) || $pkg;
+    my $filter = shift;
+    my $func = shift;
+    my $params = shift || [];
+
+    return $func if (grep { $_ eq $func } @{$pkg->filter_normalizers->{$filter}});
+
+    push(@{$pkg->filter_normalizers->{$filter}}, { function => $func, params => $params });
+
+    return $func;
+}
+
+sub filter_normalizers {
+    my $pkg = shift;
+    $pkg = ref($pkg) || $pkg;
+
+    my $filter = shift;
+
+    $parser_config{$pkg}{filter_normalizers} ||= {};
+    if ($filter) {
+        $parser_config{$pkg}{filter_normalizers}{$filter} ||= [];
+        return $parser_config{$pkg}{filter_normalizers}{$filter};
+    }
+
+    return $parser_config{$pkg}{filter_normalizers};
+}
+
 sub default_search_class {
     my $pkg = shift;
     $pkg = ref($pkg) || $pkg;
index 8a06ceb..f705d81 100644 (file)
@@ -299,7 +299,7 @@ sub cn_browse {
        if ($page <= 0) {
                my $before = $_storage->request(
                        "open-ils.cstore.direct.asset.call_number.search.atomic",
-                       { label         => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
+                       { label         => { "<" => $label },
                          owning_lib    => \@ou_ids,
               deleted => 'f',
               @cp_filter
@@ -346,7 +346,7 @@ Returns the XML representation of the requested bibliographic record's holdings
                  params   =>
                        [
                                { name => 'label',
-                                 desc => 'The target call number lable',
+                                 desc => 'The target call number label',
                                  type => 'string' },
                                { name => 'org_unit',
                                  desc => 'The org unit shortname (or "-" or undef for global) to browse',
@@ -422,7 +422,7 @@ sub cn_startwith {
        if ($page < 0) {
                my $before = $_storage->request(
                        "open-ils.cstore.direct.asset.call_number.search.atomic",
-                       { label         => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
+                       { label         => { "<" => $label },
                          owning_lib    => \@ou_ids,
               deleted => 'f',
               @cp_filter
@@ -469,7 +469,7 @@ Returns the XML representation of the requested bibliographic record's holdings
                  params   =>
                        [
                                { name => 'label',
-                                 desc => 'The target call number lable',
+                                 desc => 'The target call number label',
                                  type => 'string' },
                                { name => 'org_unit',
                                  desc => 'The org unit shortname (or "-" or undef for global) to browse',
@@ -2841,6 +2841,49 @@ Returns the ISBN list for the metarecord of the requested isbn
                }
 );
 
+sub return_bib_search_aliases {
+    my %aliases;
+
+       my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
+
+       my $cmsa = $_storage->request(
+               'open-ils.cstore.direct.config.metabib_search_alias.search.atomic',
+               { alias => { '!=' => undef } }
+       )->gather(1);
+
+    foreach (@$cmsa) {
+        if ($_->alias =~ /\./) {
+            my ($qualifier, $name) = $_->alias =~ m/^(.+?)\.(.+)$/;
+            $aliases{$qualifier}{$name}{'index'} = $_->alias;
+            # We will add a 'title' property in a subsequent schema
+            $aliases{$qualifier}{$name}{'title'} = $name;
+        } else {
+            # au/kw/se/su/ti go into the default 'eg' qualifier
+            $aliases{'eg'}{$_->alias}{'index'} = $_->alias;
+            $aliases{'eg'}{$_->alias}{'title'} = $_->alias;
+        }
+    }
+
+    return \%aliases;
+}
+
+__PACKAGE__->register_method(
+       method    => 'return_bib_search_aliases',
+       api_name  => 'open-ils.supercat.biblio.search_aliases',
+       api_level => 1,
+       argc      => 0,
+       signature =>
+               { desc     => <<"                 DESC",
+Returns the set of qualified search aliases in the system
+                 DESC
+                 params   => [ ],
+                 'return' =>
+                       { desc => 'Hash of qualified search aliases',
+                         type => 'object' }
+               }
+);
+
+
 package OpenILS::Application::SuperCat::unAPI;
 use base qw/OpenILS::Application::SuperCat/;
 
index ae20208..107fc35 100644 (file)
@@ -34,118 +34,157 @@ my $log = 'OpenSRF::Utils::Logger';
 my $U = 'OpenILS::Application::AppUtils';
 
 # set the bootstrap config when this module is loaded
-my ($bootstrap, $supercat, $actor, $parser, $search, $xslt, $cn_browse_xslt, %browse_types);
+my ($bootstrap, $supercat, $actor, $parser, $search, $xslt, $cn_browse_xslt, %browse_types, %qualifier_map);
 
 $browse_types{call_number}{xml} = sub {
-       my $tree = shift;
+    my $tree = shift;
 
-       my $year = (gmtime())[5] + 1900;
-       my $content = '';
+    my $year = (gmtime())[5] + 1900;
+    my $content = '';
 
-       $content .= "<volumes  xmlns='http://open-ils.org/spec/holdings/v1'>\n";
+    $content .= "<volumes  xmlns='http://open-ils.org/spec/holdings/v1'>\n";
 
-       for my $cn (@$tree) {
-               (my $cn_class = $cn->class_name) =~ s/::/-/gso;
-               $cn_class =~ s/Fieldmapper-//gso;
+    for my $cn (@$tree) {
+        (my $cn_class = $cn->class_name) =~ s/::/-/gso;
+        $cn_class =~ s/Fieldmapper-//gso;
 
-               my $cn_tag = "tag:open-ils.org,$year:$cn_class/".$cn->id;
-               my $cn_lib = $cn->owning_lib->shortname;
-               my $cn_label = $cn->label;
+        my $cn_tag = "tag:open-ils.org,$year:$cn_class/".$cn->id;
+        my $cn_lib = $cn->owning_lib->shortname;
+        my $cn_label = $cn->label;
 
-               $cn_label =~ s/\n//gos;
-               $cn_label =~ s/&/&amp;/go;
-               $cn_label =~ s/'/&apos;/go;
-               $cn_label =~ s/</&lt;/go;
-               $cn_label =~ s/>/&gt;/go;
+        $cn_label =~ s/\n//gos;
+        $cn_label =~ s/&/&amp;/go;
+        $cn_label =~ s/'/&apos;/go;
+        $cn_label =~ s/</&lt;/go;
+        $cn_label =~ s/>/&gt;/go;
 
-               (my $ou_class = $cn->owning_lib->class_name) =~ s/::/-/gso;
-               $ou_class =~ s/Fieldmapper-//gso;
+        (my $ou_class = $cn->owning_lib->class_name) =~ s/::/-/gso;
+        $ou_class =~ s/Fieldmapper-//gso;
 
-               my $ou_tag = "tag:open-ils.org,$year:$ou_class/".$cn->owning_lib->id;
-               my $ou_name = $cn->owning_lib->name;
+        my $ou_tag = "tag:open-ils.org,$year:$ou_class/".$cn->owning_lib->id;
+        my $ou_name = $cn->owning_lib->name;
 
-               $ou_name =~ s/\n//gos;
-               $ou_name =~ s/'/&apos;/go;
+        $ou_name =~ s/\n//gos;
+        $ou_name =~ s/'/&apos;/go;
 
-               (my $rec_class = $cn->record->class_name) =~ s/::/-/gso;
-               $rec_class =~ s/Fieldmapper-//gso;
+        (my $rec_class = $cn->record->class_name) =~ s/::/-/gso;
+        $rec_class =~ s/Fieldmapper-//gso;
 
-               my $rec_tag = "tag:open-ils.org,$year:$rec_class/".$cn->record->id.'/'.$cn->owning_lib->shortname;
+        my $rec_tag = "tag:open-ils.org,$year:$rec_class/".$cn->record->id.'/'.$cn->owning_lib->shortname;
 
-               $content .= "<volume id='$cn_tag' lib='$cn_lib' label='$cn_label'>\n";
-               $content .= "<owning_lib xmlns='http://open-ils.org/spec/actors/v1' id='$ou_tag' name='$ou_name'/>\n";
+        $content .= "<volume id='$cn_tag' lib='$cn_lib' label='$cn_label'>\n";
+        $content .= "<owning_lib xmlns='http://open-ils.org/spec/actors/v1' id='$ou_tag' name='$ou_name'/>\n";
 
-               my $r_doc = $parser->parse_string($cn->record->marc);
-               $r_doc->documentElement->setAttribute( id => $rec_tag );
-               $content .= $U->entityize($r_doc->documentElement->toString);
+        my $r_doc = $parser->parse_string($cn->record->marc);
+        $r_doc->documentElement->setAttribute( id => $rec_tag );
+        $content .= $U->entityize($r_doc->documentElement->toString);
 
-               $content .= "</volume>\n";
-       }
+        $content .= "</volume>\n";
+    }
 
-       $content .= "</volumes>\n";
-       return ("Content-type: application/xml\n\n",$content);
+    $content .= "</volumes>\n";
+    return ("Content-type: application/xml\n\n",$content);
 };
 
 
 $browse_types{call_number}{html} = sub {
-       my $tree = shift;
-       my $p = shift;
-       my $n = shift;
-
-       if (!$cn_browse_xslt) {
-               $cn_browse_xslt = $parser->parse_file(
-                       OpenSRF::Utils::SettingsClient
-                               ->new
-                               ->config_value( dirs => 'xsl' ).
-                       "/CNBrowse2HTML.xsl"
-               );
-               $cn_browse_xslt = $xslt->parse_stylesheet( $cn_browse_xslt );
-       }
-
-       my (undef,$xml) = $browse_types{call_number}{xml}->($tree);
-
-       return (
-               "Content-type: text/html\n\n",
-               $U->entityize(
-                       $cn_browse_xslt->transform(
-                               $parser->parse_string( $xml ),
-                               'prev' => "'$p'",
-                               'next' => "'$n'"
-                       )->toString(1)
-               )
-       );
+    my $tree = shift;
+    my $p = shift;
+    my $n = shift;
+
+    if (!$cn_browse_xslt) {
+        $cn_browse_xslt = $parser->parse_file(
+                OpenSRF::Utils::SettingsClient
+                        ->new
+                        ->config_value( dirs => 'xsl' ).
+                "/CNBrowse2HTML.xsl"
+        );
+        $cn_browse_xslt = $xslt->parse_stylesheet( $cn_browse_xslt );
+    }
+
+    my (undef,$xml) = $browse_types{call_number}{xml}->($tree);
+
+    return (
+        "Content-type: text/html\n\n",
+        $U->entityize(
+            $cn_browse_xslt->transform(
+                $parser->parse_string( $xml ),
+                'prev' => "'$p'",
+                'next' => "'$n'"
+            )->toString(1)
+        )
+    );
 };
 
 sub import {
-       my $self = shift;
-       $bootstrap = shift;
+    my $self = shift;
+    $bootstrap = shift;
 }
 
 
 sub child_init {
-       OpenSRF::System->bootstrap_client( config_file => $bootstrap );
-       
-       my $idl = OpenSRF::Utils::SettingsClient->new->config_value("IDL");
-       Fieldmapper->import(IDL => $idl);
+    OpenSRF::System->bootstrap_client( config_file => $bootstrap );
+    
+    my $idl = OpenSRF::Utils::SettingsClient->new->config_value("IDL");
+    Fieldmapper->import(IDL => $idl);
+
+    $supercat = OpenSRF::AppSession->create('open-ils.supercat');
+    $actor = OpenSRF::AppSession->create('open-ils.actor');
+    $search = OpenSRF::AppSession->create('open-ils.search');
+    $parser = new XML::LibXML;
+    $xslt = new XML::LibXSLT;
+
+    $cn_browse_xslt = $parser->parse_file(
+            OpenSRF::Utils::SettingsClient
+                    ->new
+                    ->config_value( dirs => 'xsl' ).
+            "/CNBrowse2HTML.xsl"
+    );
 
-       $supercat = OpenSRF::AppSession->create('open-ils.supercat');
-       $actor = OpenSRF::AppSession->create('open-ils.actor');
-       $search = OpenSRF::AppSession->create('open-ils.search');
-       $parser = new XML::LibXML;
-       $xslt = new XML::LibXSLT;
+    $cn_browse_xslt = $xslt->parse_stylesheet( $cn_browse_xslt );
 
-        $cn_browse_xslt = $parser->parse_file(
-                OpenSRF::Utils::SettingsClient
-                        ->new
-                        ->config_value( dirs => 'xsl' ).
-                "/CNBrowse2HTML.xsl"
-        );
+    %qualifier_map = %{$supercat
+        ->request("open-ils.supercat.biblio.search_aliases")
+        ->gather(1)};
 
-       $cn_browse_xslt = $xslt->parse_stylesheet( $cn_browse_xslt );
+    my %attribute_desc = (
+        site        => 'Evergreen Site Code (shortname)',
+        sort        => 'Sort on relevance, title, author, pubdate, create_date or edit_date',
+        dir         => 'Sort direction (asc|desc)',
+        available   => 'Filter to available (true|false)',
+    );
 
-       my $list = $supercat
-               ->request("open-ils.supercat.record.formats")
-               ->gather(1);
+    # Append the non-search-alias attributes to the qualifier map
+    foreach ( qw/
+            available
+            ascending
+            descending
+            sort
+            format
+            before
+            after
+            statuses
+            locations
+            site
+            depth
+            lasso
+            offset
+            limit
+            preferred_language
+            preferred_language_weight
+            preferred_language_multiplier
+        /) {
+        $qualifier_map{'eg'}{$_}{'index'} = $_;
+        if (exists $attribute_desc{$_}) {
+            $qualifier_map{'eg'}{$_}{'title'} = $attribute_desc{$_};
+        } else {
+            $qualifier_map{'eg'}{$_}{'title'} = $_;
+        }
+    }
+
+    my $list = $supercat
+        ->request("open-ils.supercat.record.formats")
+        ->gather(1);
 
     $list = [ map { (keys %$_)[0] } @$list ];
     push @$list, 'htmlholdings','html', 'marctxt', 'ris';
@@ -157,22 +196,22 @@ sub child_init {
                 my $__a = $browse_axis;
 
                 $browse_types{$__a}{$__f} = sub {
-                       my $record_list = shift;
-                       my $prev = shift;
-                       my $next = shift;
-                       my $real_format = shift || $__f;
-                       my $unapi = shift;
-                       my $base = shift;
-                       my $site = shift;
-
-                                       $log->info("Creating record feed with params [$real_format, $record_list, $unapi, $site]");
-                       my $feed = create_record_feed( 'record', $real_format, $record_list, $unapi, $site, undef, $real_format =~ /(-full|-uris)$/o ? 1 : 0 );
-                       $feed->root( "$base/../" );
-                       $feed->lib( $site );
-                       $feed->link( next => $next => $feed->type );
-                       $feed->link( previous => $prev => $feed->type );
-
-                       return (
+                    my $record_list = shift;
+                    my $prev = shift;
+                    my $next = shift;
+                    my $real_format = shift || $__f;
+                    my $unapi = shift;
+                    my $base = shift;
+                    my $site = shift;
+
+                    $log->info("Creating record feed with params [$real_format, $record_list, $unapi, $site]");
+                    my $feed = create_record_feed( 'record', $real_format, $record_list, $unapi, $site, undef, $real_format =~ /(-full|-uris)$/o ? 1 : 0 );
+                    $feed->root( "$base/../" );
+                    $feed->lib( $site );
+                    $feed->link( next => $next => $feed->type );
+                    $feed->link( previous => $prev => $feed->type );
+
+                    return (
                         "Content-type: ". $feed->type ."; charset=utf-8\n\n",
                         $feed->toString
                     );
@@ -188,21 +227,21 @@ sub child_init {
                 my $__a = $browse_axis;
 
                 $browse_types{$__a}{$__f} = sub {
-                       my $record_list = shift;
-                       my $prev = shift;
-                       my $next = shift;
-                       my $real_format = shift || $__f;
-                       my $unapi = shift;
-                       my $base = shift;
-                       my $site = shift;
-
-                                       $log->info("Creating record feed with params [$real_format, $record_list, $unapi, $site]");
-                       my $feed = create_record_feed( 'authority', $real_format, $record_list, $unapi, $site, undef, $real_format =~ /-full$/o ? -1 : 0 );
-                       $feed->root( "$base/../" );
-                       $feed->link( next => $next => $feed->type );
-                       $feed->link( previous => $prev => $feed->type );
-
-                       return (
+                    my $record_list = shift;
+                    my $prev = shift;
+                    my $next = shift;
+                    my $real_format = shift || $__f;
+                    my $unapi = shift;
+                    my $base = shift;
+                    my $site = shift;
+
+                    $log->info("Creating record feed with params [$real_format, $record_list, $unapi, $site]");
+                    my $feed = create_record_feed( 'authority', $real_format, $record_list, $unapi, $site, undef, $real_format =~ /-full$/o ? -1 : 0 );
+                    $feed->root( "$base/../" );
+                    $feed->link( next => $next => $feed->type );
+                    $feed->link( previous => $prev => $feed->type );
+
+                    return (
                         "Content-type: ". $feed->type ."; charset=utf-8\n\n",
                         $feed->toString
                     );
@@ -228,18 +267,18 @@ Otherwise, we won't return any holdings.
 =cut
 
 sub parse_feed_type {
-       my $type = shift;
+    my $type = shift;
 
-       if ($type =~ /-full$/o) {
-               return 1;
-       }
+     if ($type =~ /-full$/o) {
+        return 1;
+    }
 
-       if ($type =~ /-uris$/o) {
-               return "uris";
-       }
+     if ($type =~ /-uris$/o) {
+        return "uris";
+    }
 
-       # Otherwise, we'll return just the facts, ma'am
-       return 0;
+    # Otherwise, we'll return just the facts, ma'am
+    return 0;
 }
 
 =head2 supercat_format($format_hashref, $format_type)
@@ -254,21 +293,21 @@ have to populate the hash with redundant information.
 =cut
 
 sub supercat_format {
-       my $h = shift;
-       my $type = shift;
+    my $h = shift;
+    my $type = shift;
 
-       (my $base_type = $type) =~ s/(-full|-uris)$//o;
+    (my $base_type = $type) =~ s/(-full|-uris)$//o;
 
-       my $format = "<format><name>$type</name><type>application/xml</type>";
+    my $format = "<format><name>$type</name><type>application/xml</type>";
 
-       for my $part ( qw/namespace_uri docs schema_location/ ) {
-               $format .= "<$part>$$h{$base_type}{$part}</$part>"
-                       if ($$h{$base_type}{$part});
-       }
+    for my $part ( qw/namespace_uri docs schema_location/ ) {
+        $format .= "<$part>$$h{$base_type}{$part}</$part>"
+            if ($$h{$base_type}{$part});
+    }
 
-       $format .= '</format>';
+    $format .= '</format>';
 
-       return $format;
+    return $format;
 }
 
 =head2 unapi_format($format_hashref, $format_type)
@@ -283,199 +322,199 @@ have to populate the hash with redundant information.
 =cut
 
 sub unapi_format {
-       my $h = shift;
-       my $type = shift;
+    my $h = shift;
+    my $type = shift;
 
-       (my $base_type = $type) =~ s/(-full|-uris)$//o;
+    (my $base_type = $type) =~ s/(-full|-uris)$//o;
 
-       my $format = "<format name='$type' type='application/xml'";
+    my $format = "<format name='$type' type='application/xml'";
 
-       for my $part ( qw/namespace_uri docs schema_location/ ) {
-               $format .= " $part='$$h{$base_type}{$part}'"
-                       if ($$h{$base_type}{$part});
-       }
+    for my $part ( qw/namespace_uri docs schema_location/ ) {
+        $format .= " $part='$$h{$base_type}{$part}'"
+            if ($$h{$base_type}{$part});
+    }
 
-       $format .= "/>\n";
+    $format .= "/>\n";
 
-       return $format;
+    return $format;
 }
 
 
 sub oisbn {
 
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
 
-       (my $isbn = $apache->path_info) =~ s{^.*?([^/]+)$}{$1}o;
+    (my $isbn = $apache->path_info) =~ s{^.*?([^/]+)$}{$1}o;
 
-       my $list = $supercat
-               ->request("open-ils.supercat.oisbn", $isbn)
-               ->gather(1);
+    my $list = $supercat
+        ->request("open-ils.supercat.oisbn", $isbn)
+        ->gather(1);
 
-       print "Content-type: application/xml; charset=utf-8\n\n";
-       print "<?xml version='1.0' encoding='UTF-8' ?>\n";
+    print "Content-type: application/xml; charset=utf-8\n\n";
+    print "<?xml version='1.0' encoding='UTF-8' ?>\n";
 
-       unless (exists $$list{metarecord}) {
-               print '<idlist/>';
-               return Apache2::Const::OK;
-       }
+    unless (exists $$list{metarecord}) {
+        print '<idlist/>';
+        return Apache2::Const::OK;
+    }
 
-       print "<idlist metarecord='$$list{metarecord}'>\n";
+    print "<idlist metarecord='$$list{metarecord}'>\n";
 
-       for ( keys %{ $$list{record_list} } ) {
-               (my $o = $$list{record_list}{$_}) =~s/^(\S+).*?$/$1/o;
-               print "  <isbn record='$_'>$o</isbn>\n"
-       }
+    for ( keys %{ $$list{record_list} } ) {
+        (my $o = $$list{record_list}{$_}) =~s/^(\S+).*?$/$1/o;
+        print "  <isbn record='$_'>$o</isbn>\n"
+    }
 
-       print "</idlist>\n";
+    print "</idlist>\n";
 
-       return Apache2::Const::OK;
+    return Apache2::Const::OK;
 }
 
 sub unapi {
 
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
 
-       my $cgi = new CGI;
+    my $cgi = new CGI;
 
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
 
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'unapi', $url)[0];
-       my $base = (split 'unapi', $url)[0] . 'unapi';
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'unapi', $url)[0];
+    my $base = (split 'unapi', $url)[0] . 'unapi';
 
 
-       my $uri = $cgi->param('id') || '';
-       my $host = $cgi->virtual_host || $cgi->server_name;
+    my $uri = $cgi->param('id') || '';
+    my $host = $cgi->virtual_host || $cgi->server_name;
 
-       my $skin = $cgi->param('skin') || 'default';
-       my $locale = $cgi->param('locale') || 'en-US';
+    my $skin = $cgi->param('skin') || 'default';
+    my $locale = $cgi->param('locale') || 'en-US';
+
+    # Enable localized results of copy status, etc
+    $supercat->session_locale($locale);
+
+    my $format = $cgi->param('format');
+    my $flesh_feed = parse_feed_type($format);
+    (my $base_format = $format) =~ s/(-full|-uris)$//o;
+    my ($id,$type,$command,$lib,$depth,$paging) = ('','','');
+
+    if (!$format) {
+        my $body = "Content-type: application/xml; charset=utf-8\n\n";
+    
+        if ($uri =~ m{^tag:[^:]+:([^\/]+)/([^\/[]+)(?:\[([0-9,]+)\])?(?:/(.+))?}o) {
+            $id = $2;
+            $paging = $3;
+            ($lib,$depth) = split('/', $4);
+            $type = 'record';
+            $type = 'metarecord' if ($1 =~ /^m/o);
+            $type = 'authority' if ($1 =~ /^authority/o);
+
+            my $list = $supercat
+                ->request("open-ils.supercat.$type.formats")
+                ->gather(1);
+
+            if ($type eq 'record' or $type eq 'isbn') {
+                $body .= <<"                FORMATS";
+<formats id='$uri'>
+    <format name='opac' type='text/html'/>
+    <format name='html' type='text/html'/>
+    <format name='htmlholdings' type='text/html'/>
+    <format name='holdings_xml' type='application/xml'/>
+    <format name='holdings_xml-full' type='application/xml'/>
+    <format name='html-full' type='text/html'/>
+    <format name='htmlholdings-full' type='text/html'/>
+    <format name='marctxt' type='text/plain'/>
+    <format name='ris' type='text/plain'/>
+                FORMATS
+            } elsif ($type eq 'metarecord') {
+                $body .= <<"                FORMATS";
+                <formats id='$uri'>
+                    <format name='opac' type='text/html'/>
+                FORMATS
+            } else {
+                $body .= <<"                FORMATS";
+                <formats id='$uri'>
+                FORMATS
+            }
 
-       # Enable localized results of copy status, etc
-       $supercat->session_locale($locale);
+            for my $h (@$list) {
+                my ($type) = keys %$h;
+                $body .= unapi_format($h, $type);
 
-       my $format = $cgi->param('format');
-       my $flesh_feed = parse_feed_type($format);
-       (my $base_format = $format) =~ s/(-full|-uris)$//o;
-       my ($id,$type,$command,$lib,$depth,$paging) = ('','','');
+                if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
+                    $body .= unapi_format($h, "$type-full");
+                    $body .= unapi_format($h, "$type-uris");
+                }
+            }
 
-       if (!$format) {
-               my $body = "Content-type: application/xml; charset=utf-8\n\n";
-       
-               if ($uri =~ m{^tag:[^:]+:([^\/]+)/([^\/[]+)(?:\[([0-9,]+)\])?(?:/(.+))?}o) {
-                       $id = $2;
-                       $paging = $3;
-                       ($lib,$depth) = split('/', $4);
-                       $type = 'record';
-                       $type = 'metarecord' if ($1 =~ /^m/o);
-                       $type = 'authority' if ($1 =~ /^authority/o);
+            $body .= "</formats>\n";
 
-                       my $list = $supercat
-                               ->request("open-ils.supercat.$type.formats")
-                               ->gather(1);
+        } else {
+            my $list = $supercat
+                ->request("open-ils.supercat.$type.formats")
+                ->gather(1);
+                
+            push @$list,
+                @{ $supercat
+                    ->request("open-ils.supercat.metarecord.formats")
+                    ->gather(1);
+                };
 
-                       if ($type eq 'record' or $type eq 'isbn') {
-                               $body .= <<"                            FORMATS";
-<formats id='$uri'>
-       <format name='opac' type='text/html'/>
-       <format name='html' type='text/html'/>
-       <format name='htmlholdings' type='text/html'/>
-       <format name='holdings_xml' type='application/xml'/>
-       <format name='holdings_xml-full' type='application/xml'/>
-       <format name='html-full' type='text/html'/>
-       <format name='htmlholdings-full' type='text/html'/>
-       <format name='marctxt' type='text/plain'/>
-       <format name='ris' type='text/plain'/>
-                               FORMATS
-                       } elsif ($type eq 'metarecord') {
-                               $body .= <<"                            FORMATS";
-                               <formats id='$uri'>
-                                       <format name='opac' type='text/html'/>
-                               FORMATS
-                       } else {
-                               $body .= <<"                            FORMATS";
-                               <formats id='$uri'>
-                               FORMATS
-                       }
-
-                       for my $h (@$list) {
-                               my ($type) = keys %$h;
-                               $body .= unapi_format($h, $type);
-
-                               if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
-                                       $body .= unapi_format($h, "$type-full");
-                                       $body .= unapi_format($h, "$type-uris");
-                               }
-                       }
-
-                       $body .= "</formats>\n";
-
-               } else {
-                       my $list = $supercat
-                               ->request("open-ils.supercat.$type.formats")
-                               ->gather(1);
-                               
-                       push @$list,
-                               @{ $supercat
-                                       ->request("open-ils.supercat.metarecord.formats")
-                                       ->gather(1);
-                               };
-
-                       my %hash = map { ( (keys %$_)[0] => (values %$_)[0] ) } @$list;
-                       $list = [ map { { $_ => $hash{$_} } } sort keys %hash ];
-
-                       $body .= <<"                    FORMATS";
+            my %hash = map { ( (keys %$_)[0] => (values %$_)[0] ) } @$list;
+            $list = [ map { { $_ => $hash{$_} } } sort keys %hash ];
+
+            $body .= <<"            FORMATS";
 <formats>
-       <format name='opac' type='text/html'/>
-       <format name='html' type='text/html'/>
-       <format name='htmlholdings' type='text/html'/>
-       <format name='holdings_xml' type='application/xml'/>
-       <format name='holdings_xml-full' type='application/xml'/>
-       <format name='html-full' type='text/html'/>
-       <format name='htmlholdings-full' type='text/html'/>
-       <format name='marctxt' type='text/plain'/>
-       <format name='ris' type='text/plain'/>
-                       FORMATS
-
-
-                       for my $h (@$list) {
-                               my ($type) = keys %$h;
-                               $body .= "\t" . unapi_format($h, $type);
-
-                               if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
-                                       $body .= "\t" . unapi_format($h, "$type-full");
-                                       $body .= "\t" . unapi_format($h, "$type-uris");
-                               }
-                       }
-
-                       $body .= "</formats>\n";
-
-               }
-               print $body;
-               return Apache2::Const::OK;
-       }
-
-       my $scheme;
-       if ($uri =~ m{^tag:[^:]+:([^\/]+)/([^\/[]+)(?:\[([0-9,]+)\])?(?:/(.+))?}o) {
-               $scheme = $1;
-               $id = $2;
-               $paging = $3;
-               ($lib,$depth) = split('/', $4);
-               $type = 'record';
-               $type = 'metarecord' if ($scheme =~ /^metabib/o);
-               $type = 'isbn' if ($scheme =~ /^isbn/o);
-               $type = 'acp' if ($scheme =~ /^asset-copy/o);
-               $type = 'acn' if ($scheme =~ /^asset-call_number/o);
-               $type = 'auri' if ($scheme =~ /^asset-uri/o);
-               $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/);
-       }
+    <format name='opac' type='text/html'/>
+    <format name='html' type='text/html'/>
+    <format name='htmlholdings' type='text/html'/>
+    <format name='holdings_xml' type='application/xml'/>
+    <format name='holdings_xml-full' type='application/xml'/>
+    <format name='html-full' type='text/html'/>
+    <format name='htmlholdings-full' type='text/html'/>
+    <format name='marctxt' type='text/plain'/>
+    <format name='ris' type='text/plain'/>
+            FORMATS
+
+
+            for my $h (@$list) {
+                my ($type) = keys %$h;
+                $body .= "\t" . unapi_format($h, $type);
+
+                if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
+                    $body .= "\t" . unapi_format($h, "$type-full");
+                    $body .= "\t" . unapi_format($h, "$type-uris");
+                }
+            }
+
+            $body .= "</formats>\n";
+
+        }
+        print $body;
+        return Apache2::Const::OK;
+    }
+
+    my $scheme;
+    if ($uri =~ m{^tag:[^:]+:([^\/]+)/([^\/[]+)(?:\[([0-9,]+)\])?(?:/(.+))?}o) {
+        $scheme = $1;
+        $id = $2;
+        $paging = $3;
+        ($lib,$depth) = split('/', $4);
+        $type = 'record';
+        $type = 'metarecord' if ($scheme =~ /^metabib/o);
+        $type = 'isbn' if ($scheme =~ /^isbn/o);
+        $type = 'acp' if ($scheme =~ /^asset-copy/o);
+        $type = 'acn' if ($scheme =~ /^asset-call_number/o);
+        $type = 'auri' if ($scheme =~ /^asset-uri/o);
+        $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/);
+    }
 
     if ($paging) {
         $paging = [split ',', $paging];
@@ -483,563 +522,563 @@ sub unapi {
         $paging = [];
     }
 
-       if (!$lib || $lib eq '-') {
-               $lib = $actor->request(
-                       'open-ils.actor.org_unit_list.search' => parent_ou => undef
-               )->gather(1)->[0]->shortname;
-       }
-
-       my ($lib_object,$lib_id,$ou_types,$lib_depth);
-       if ($type ne 'acn' && $type ne 'acp' && $type ne 'auri') {
-               $lib_object = $actor->request(
-                       'open-ils.actor.org_unit_list.search' => shortname => $lib
-               )->gather(1)->[0];
-               $lib_id = $lib_object->id;
-
-               $ou_types = $actor->request( 'open-ils.actor.org_types.retrieve' )->gather(1);
-               $lib_depth = $depth || (grep { $_->id == $lib_object->ou_type } @$ou_types)[0]->depth;
-       }
-
-       if ($command eq 'browse') {
-               print "Location: $root/browse/$base_format/$scheme/$lib/$id\n\n";
-               return 302;
-       }
-
-       if ($type eq 'isbn') {
-               my $rec = $supercat->request('open-ils.supercat.isbn.object.retrieve',$id)->gather(1);
-               if (!@$rec) {
-                       print "Content-type: text/html; charset=utf-8\n\n";
-                       $apache->custom_response( 404, <<"                      HTML");
-                       <html>
-                               <head>
-                                       <title>Type [$type] with id [$id] not found!</title>
-                               </head>
-                               <body>
-                                       <br/>
-                                       <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
-                               </body>
-                       </html>
-                       HTML
-                       return 404;
-               }
-               $id = $rec->[0]->id;
-               $type = 'record';
-       }
-
-       if ( !grep
-                  { (keys(%$_))[0] eq $base_format }
-                  @{ $supercat->request("open-ils.supercat.$type.formats")->gather(1) }
-                and !grep
-                  { $_ eq $base_format }
-                  qw/opac html htmlholdings marctxt ris holdings_xml/
-       ) {
-               print "Content-type: text/html; charset=utf-8\n\n";
-               $apache->custom_response( 406, <<"              HTML");
-               <html>
-                       <head>
-                               <title>Invalid format [$format] for type [$type]!</title>
-                       </head>
-                       <body>
-                               <br/>
-                               <center>Sorry, format $format is not valid for type $type.</center>
-                       </body>
-               </html>
-               HTML
-               return 406;
-       }
-
-       if ($format eq 'opac') {
-               print "Location: $root/../../$locale/skin/$skin/xml/rresult.xml?m=$id&l=$lib_id&d=$lib_depth\n\n"
-                       if ($type eq 'metarecord');
-               print "Location: $root/../../$locale/skin/$skin/xml/rdetail.xml?r=$id&l=$lib_id&d=$lib_depth\n\n"
-                       if ($type eq 'record');
-               return 302;
-       } elsif (OpenILS::WWW::SuperCat::Feed->exists($base_format) && ($type ne 'acn' && $type ne 'acp' && $type ne 'auri')) {
-               my $feed = create_record_feed(
-                       $type,
-                       $format => [ $id ],
-                       $base,
-                       $lib,
-                       $depth,
-                       $flesh_feed,
+    if (!$lib || $lib eq '-') {
+         $lib = $actor->request(
+            'open-ils.actor.org_unit_list.search' => parent_ou => undef
+        )->gather(1)->[0]->shortname;
+    }
+
+    my ($lib_object,$lib_id,$ou_types,$lib_depth);
+    if ($type ne 'acn' && $type ne 'acp' && $type ne 'auri') {
+        $lib_object = $actor->request(
+            'open-ils.actor.org_unit_list.search' => shortname => $lib
+        )->gather(1)->[0];
+        $lib_id = $lib_object->id;
+
+        $ou_types = $actor->request( 'open-ils.actor.org_types.retrieve' )->gather(1);
+        $lib_depth = defined($depth) ? $depth : (grep { $_->id == $lib_object->ou_type } @$ou_types)[0]->depth;
+    }
+
+    if ($command eq 'browse') {
+        print "Location: $root/browse/$base_format/$scheme/$lib/$id\n\n";
+        return 302;
+    }
+
+    if ($type eq 'isbn') {
+        my $rec = $supercat->request('open-ils.supercat.isbn.object.retrieve',$id)->gather(1);
+        if (!@$rec) {
+            print "Content-type: text/html; charset=utf-8\n\n";
+            $apache->custom_response( 404, <<"            HTML");
+            <html>
+                <head>
+                    <title>Type [$type] with id [$id] not found!</title>
+                </head>
+                <body>
+                    <br/>
+                    <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
+                </body>
+            </html>
+            HTML
+            return 404;
+        }
+        $id = $rec->[0]->id;
+        $type = 'record';
+    }
+
+    if ( !grep
+           { (keys(%$_))[0] eq $base_format }
+           @{ $supercat->request("open-ils.supercat.$type.formats")->gather(1) }
+         and !grep
+           { $_ eq $base_format }
+           qw/opac html htmlholdings marctxt ris holdings_xml/
+    ) {
+        print "Content-type: text/html; charset=utf-8\n\n";
+        $apache->custom_response( 406, <<"        HTML");
+        <html>
+            <head>
+                <title>Invalid format [$format] for type [$type]!</title>
+            </head>
+            <body>
+                <br/>
+                <center>Sorry, format $format is not valid for type $type.</center>
+            </body>
+        </html>
+        HTML
+        return 406;
+    }
+
+    if ($format eq 'opac') {
+        print "Location: $root/../../$locale/skin/$skin/xml/rresult.xml?m=$id&l=$lib_id&d=$lib_depth\n\n"
+            if ($type eq 'metarecord');
+        print "Location: $root/../../$locale/skin/$skin/xml/rdetail.xml?r=$id&l=$lib_id&d=$lib_depth\n\n"
+            if ($type eq 'record');
+        return 302;
+    } elsif (OpenILS::WWW::SuperCat::Feed->exists($base_format) && ($type ne 'acn' && $type ne 'acp' && $type ne 'auri')) {
+        my $feed = create_record_feed(
+            $type,
+            $format => [ $id ],
+            $base,
+            $lib,
+            $depth,
+            $flesh_feed,
             $paging
-               );
-
-               if (!$feed->count) {
-                       print "Content-type: text/html; charset=utf-8\n\n";
-                       $apache->custom_response( 404, <<"                      HTML");
-                       <html>
-                               <head>
-                                       <title>Type [$type] with id [$id] not found!</title>
-                               </head>
-                               <body>
-                                       <br/>
-                                       <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
-                               </body>
-                       </html>
-                       HTML
-                       return 404;
-               }
-
-               $feed->root($root);
-               $feed->creator($host);
-               $feed->update_ts();
-               $feed->link( unapi => $base) if ($flesh_feed);
-
-               print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
-               print $U->entityize($feed->toString) . "\n";
-
-               return Apache2::Const::OK;
-       }
-
-       my $method = "open-ils.supercat.$type.$base_format.$command";
-       my @params = ($id);
-       push @params, $lib, $lib_depth, $flesh_feed, $paging if ($base_format eq 'holdings_xml');
-
-       # for acn, acp, etc, the "lib" pathinfo position isn't useful.
-       # however, we can have it carry extra options like no_record! (comma separated)
-       push @params, { map { ( $_ => 1 ) } split(',', $lib) } if ( grep { $type eq $_} qw/acn acp auri/);
-
-       my $req = $supercat->request($method,@params);
-       my $data = $req->gather();
-
-       if ($req->failed || !$data) {
-               print "Content-type: text/html; charset=utf-8\n\n";
-               $apache->custom_response( 404, <<"              HTML");
-               <html>
-                       <head>
-                               <title>$type $id not found!</title>
-                       </head>
-                       <body>
-                               <br/>
-                               <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
-                       </body>
-               </html>
-               HTML
-               return 404;
-       }
-
-       print "Content-type: application/xml; charset=utf-8\n\n$data";
-
-       if ($base_format eq 'holdings_xml') {
-               while (my $c = $req->recv) {
-                       print $c->content;
-               }
-       }
-
-       return Apache2::Const::OK;
+        );
+
+        if (!$feed->count) {
+            print "Content-type: text/html; charset=utf-8\n\n";
+            $apache->custom_response( 404, <<"            HTML");
+            <html>
+                <head>
+                    <title>Type [$type] with id [$id] not found!</title>
+                </head>
+                <body>
+                    <br/>
+                    <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
+                </body>
+            </html>
+            HTML
+            return 404;
+        }
+
+        $feed->root($root);
+        $feed->creator($host);
+        $feed->update_ts();
+        $feed->link( unapi => $base) if ($flesh_feed);
+
+        print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
+        print $U->entityize($feed->toString) . "\n";
+
+        return Apache2::Const::OK;
+    }
+
+    my $method = "open-ils.supercat.$type.$base_format.$command";
+    my @params = ($id);
+    push @params, $lib, $lib_depth, $flesh_feed, $paging if ($base_format eq 'holdings_xml');
+
+    # for acn, acp, etc, the "lib" pathinfo position isn't useful.
+    # however, we can have it carry extra options like no_record! (comma separated)
+    push @params, { map { ( $_ => 1 ) } split(',', $lib) } if ( grep { $type eq $_} qw/acn acp auri/);
+
+    my $req = $supercat->request($method,@params);
+    my $data = $req->gather();
+
+    if ($req->failed || !$data) {
+        print "Content-type: text/html; charset=utf-8\n\n";
+        $apache->custom_response( 404, <<"        HTML");
+        <html>
+            <head>
+                <title>$type $id not found!</title>
+            </head>
+            <body>
+                <br/>
+                <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
+            </body>
+        </html>
+        HTML
+        return 404;
+    }
+
+    print "Content-type: application/xml; charset=utf-8\n\n$data";
+
+    if ($base_format eq 'holdings_xml') {
+        while (my $c = $req->recv) {
+            print $c->content;
+        }
+    }
+
+    return Apache2::Const::OK;
 }
 
 sub supercat {
 
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
-
-       my $cgi = new CGI;
-
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
-
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'supercat', $url)[0];
-       my $base = (split 'supercat', $url)[0] . 'supercat';
-       my $unapi = (split 'supercat', $url)[0] . 'unapi';
-
-       my $host = $cgi->virtual_host || $cgi->server_name;
-
-       my $path = $cgi->path_info;
-       my ($id,$type,$format,$command) = reverse split '/', $path;
-       my $flesh_feed = parse_feed_type($format);
-       (my $base_format = $format) =~ s/(-full|-uris)$//o;
-
-       my $skin = $cgi->param('skin') || 'default';
-       my $locale = $cgi->param('locale') || 'en-US';
-
-       # Enable localized results of copy status, etc
-       $supercat->session_locale($locale);
-       
-       if ( $path =~ m{^/formats(?:/([^\/]+))?$}o ) {
-               print "Content-type: application/xml; charset=utf-8\n";
-               if ($1) {
-                       my $list = $supercat
-                               ->request("open-ils.supercat.$1.formats")
-                               ->gather(1);
-
-                       print "\n";
-
-                       print "<formats>
-                                  <format>
-                                    <name>opac</name>
-                                    <type>text/html</type>
-                                  </format>";
-
-                       if ($1 eq 'record' or $1 eq 'isbn') {
-                               print "<format>
-                                    <name>htmlholdings</name>
-                                    <type>text/html</type>
-                                  </format>
-                                  <format>
-                                    <name>html</name>
-                                    <type>text/html</type>
-                                  </format>
-                                  <format>
-                                    <name>htmlholdings-full</name>
-                                    <type>text/html</type>
-                                  </format>
-                                  <format>
-                                    <name>html-full</name>
-                                    <type>text/html</type>
-                                  </format>
-                                  <format>
-                                    <name>marctxt</name>
-                                    <type>text/plain</type>
-                                  </format>
-                                  <format>
-                                    <name>ris</name>
-                                    <type>text/plain</type>
-                                  </format>";
-                       }
-
-                       for my $h (@$list) {
-                               my ($type) = keys %$h;
-                               print supercat_format($h, $type);
-
-                               if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
-                                       print supercat_format($h, "$type-full");
-                                       print supercat_format($h, "$type-uris");
-                               }
-
-                       }
-
-                       print "</formats>\n";
-
-                       return Apache2::Const::OK;
-               }
-
-               my $list = $supercat
-                       ->request("open-ils.supercat.record.formats")
-                       ->gather(1);
-                               
-               push @$list,
-                       @{ $supercat
-                               ->request("open-ils.supercat.metarecord.formats")
-                               ->gather(1);
-                       };
-
-               my %hash = map { ( (keys %$_)[0] => (values %$_)[0] ) } @$list;
-               $list = [ map { { $_ => $hash{$_} } } sort keys %hash ];
-
-               print "\n<formats>
-                          <format>
-                            <name>opac</name>
-                            <type>text/html</type>
-                          </format>
-                          <format>
-                            <name>htmlholdings</name>
-                            <type>text/html</type>
-                          </format>
-                          <format>
-                            <name>html</name>
-                            <type>text/html</type>
-                          </format>
-                          <format>
-                            <name>htmlholdings-full</name>
-                            <type>text/html</type>
-                          </format>
-                          <format>
-                            <name>html-full</name>
-                            <type>text/html</type>
-                          </format>
-                          <format>
-                            <name>marctxt</name>
-                            <type>text/plain</type>
-                          </format>
-                          <format>
-                            <name>ris</name>
-                            <type>text/plain</type>
-                          </format>";
-
-               for my $h (@$list) {
-                       my ($type) = keys %$h;
-                       print supercat_format($h, $type);
-
-                       if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
-                               print supercat_format($h, "$type-full");
-                               print supercat_format($h, "$type-uris");
-                       }
-
-               }
-
-               print "</formats>\n";
-
-
-               return Apache2::Const::OK;
-       }
-
-       if ($format eq 'opac') {
-               print "Location: $root/../../$locale/skin/$skin/xml/rresult.xml?m=$id\n\n"
-                       if ($type eq 'metarecord');
-               print "Location: $root/../../$locale/skin/$skin/xml/rdetail.xml?r=$id\n\n"
-                       if ($type eq 'record');
-               return 302;
-
-       } elsif ($base_format eq 'marc21') {
-
-               my $ret = 200;    
-               try {
-                       my $bib = $supercat->request( "open-ils.supercat.record.object.retrieve", $id )->gather(1)->[0];
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
+
+    my $cgi = new CGI;
+
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
+
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'supercat', $url)[0];
+    my $base = (split 'supercat', $url)[0] . 'supercat';
+    my $unapi = (split 'supercat', $url)[0] . 'unapi';
+
+    my $host = $cgi->virtual_host || $cgi->server_name;
+
+    my $path = $cgi->path_info;
+    my ($id,$type,$format,$command) = reverse split '/', $path;
+    my $flesh_feed = parse_feed_type($format);
+    (my $base_format = $format) =~ s/(-full|-uris)$//o;
+
+    my $skin = $cgi->param('skin') || 'default';
+    my $locale = $cgi->param('locale') || 'en-US';
+
+    # Enable localized results of copy status, etc
+    $supercat->session_locale($locale);
+    
+    if ( $path =~ m{^/formats(?:/([^\/]+))?$}o ) {
+        print "Content-type: application/xml; charset=utf-8\n";
+        if ($1) {
+            my $list = $supercat
+                ->request("open-ils.supercat.$1.formats")
+                ->gather(1);
+
+            print "\n";
+
+            print "<formats>
+                   <format>
+                     <name>opac</name>
+                     <type>text/html</type>
+                   </format>";
+
+            if ($1 eq 'record' or $1 eq 'isbn') {
+                print "<format>
+                     <name>htmlholdings</name>
+                     <type>text/html</type>
+                   </format>
+                   <format>
+                     <name>html</name>
+                     <type>text/html</type>
+                   </format>
+                   <format>
+                     <name>htmlholdings-full</name>
+                     <type>text/html</type>
+                   </format>
+                   <format>
+                     <name>html-full</name>
+                     <type>text/html</type>
+                   </format>
+                   <format>
+                     <name>marctxt</name>
+                     <type>text/plain</type>
+                   </format>
+                   <format>
+                     <name>ris</name>
+                     <type>text/plain</type>
+                   </format>";
+            }
+
+            for my $h (@$list) {
+                my ($type) = keys %$h;
+                print supercat_format($h, $type);
+
+                if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
+                    print supercat_format($h, "$type-full");
+                    print supercat_format($h, "$type-uris");
+                }
+
+            }
+
+            print "</formats>\n";
+
+            return Apache2::Const::OK;
+        }
+
+        my $list = $supercat
+            ->request("open-ils.supercat.record.formats")
+            ->gather(1);
+                
+        push @$list,
+            @{ $supercat
+                ->request("open-ils.supercat.metarecord.formats")
+                ->gather(1);
+            };
+
+        my %hash = map { ( (keys %$_)[0] => (values %$_)[0] ) } @$list;
+        $list = [ map { { $_ => $hash{$_} } } sort keys %hash ];
+
+        print "\n<formats>
+               <format>
+                 <name>opac</name>
+                 <type>text/html</type>
+               </format>
+               <format>
+                 <name>htmlholdings</name>
+                 <type>text/html</type>
+               </format>
+               <format>
+                 <name>html</name>
+                 <type>text/html</type>
+               </format>
+               <format>
+                 <name>htmlholdings-full</name>
+                 <type>text/html</type>
+               </format>
+               <format>
+                 <name>html-full</name>
+                 <type>text/html</type>
+               </format>
+               <format>
+                 <name>marctxt</name>
+                 <type>text/plain</type>
+               </format>
+               <format>
+                 <name>ris</name>
+                 <type>text/plain</type>
+               </format>";
+
+        for my $h (@$list) {
+            my ($type) = keys %$h;
+            print supercat_format($h, $type);
+
+            if (OpenILS::WWW::SuperCat::Feed->exists($type)) {
+                print supercat_format($h, "$type-full");
+                print supercat_format($h, "$type-uris");
+            }
+
+        }
+
+        print "</formats>\n";
+
+
+        return Apache2::Const::OK;
+    }
+
+    if ($format eq 'opac') {
+        print "Location: $root/../../$locale/skin/$skin/xml/rresult.xml?m=$id\n\n"
+            if ($type eq 'metarecord');
+        print "Location: $root/../../$locale/skin/$skin/xml/rdetail.xml?r=$id\n\n"
+            if ($type eq 'record');
+        return 302;
+
+    } elsif ($base_format eq 'marc21') {
+
+        my $ret = 200;    
+        try {
+            my $bib = $supercat->request( "open-ils.supercat.record.object.retrieve", $id )->gather(1)->[0];
         
-                       print "Content-type: application/octet-stream\n\n" . MARC::Record->new_from_xml( $bib->marc, 'UTF-8', 'USMARC' )->as_usmarc;
-
-               } otherwise {
-                       warn shift();
-                       
-                       print "Content-type: text/html; charset=utf-8\n\n";
-                       $apache->custom_response( 404, <<"                      HTML");
-                       <html>
-                               <head>
-                                       <title>ERROR</title>
-                               </head>
-                               <body>
-                                       <br/>
-                                       <center>Couldn't fetch $id as MARC21.</center>
-                               </body>
-                       </html>
-                       HTML
-                       $ret = 404;
-               };
-
-               return Apache2::Const::OK;
-
-       } elsif (OpenILS::WWW::SuperCat::Feed->exists($base_format)) {
-               my $feed = create_record_feed(
-                       $type,
-                       $format => [ $id ],
-                       undef, undef, undef,
-                       $flesh_feed
-               );
-
-               $feed->root($root);
-               $feed->creator($host);
-
-               $feed->update_ts();
-
-               $feed->link( unapi => $base) if ($flesh_feed);
-
-               print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
-               print $U->entityize($feed->toString) . "\n";
-
-               return Apache2::Const::OK;
-       }
-
-       my $req = $supercat->request("open-ils.supercat.$type.$format.$command",$id);
-       $req->wait_complete;
-
-       if ($req->failed) {
-               print "Content-type: text/html; charset=utf-8\n\n";
-               $apache->custom_response( 404, <<"              HTML");
-               <html>
-                       <head>
-                               <title>$type $id not found!</title>
-                       </head>
-                       <body>
-                               <br/>
-                               <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
-                       </body>
-               </html>
-               HTML
-               return 404;
-       }
-
-       print "Content-type: application/xml; charset=utf-8\n\n";
-       print $U->entityize( $parser->parse_string( $req->gather(1) )->documentElement->toString );
-
-       return Apache2::Const::OK;
+            print "Content-type: application/octet-stream\n\n" . MARC::Record->new_from_xml( $bib->marc, 'UTF-8', 'USMARC' )->as_usmarc;
+
+        } otherwise {
+            warn shift();
+            
+            print "Content-type: text/html; charset=utf-8\n\n";
+            $apache->custom_response( 404, <<"            HTML");
+            <html>
+                <head>
+                    <title>ERROR</title>
+                </head>
+                <body>
+                    <br/>
+                    <center>Couldn't fetch $id as MARC21.</center>
+                </body>
+            </html>
+            HTML
+            $ret = 404;
+        };
+
+        return Apache2::Const::OK;
+
+    } elsif (OpenILS::WWW::SuperCat::Feed->exists($base_format)) {
+        my $feed = create_record_feed(
+            $type,
+            $format => [ $id ],
+            undef, undef, undef,
+            $flesh_feed
+        );
+
+        $feed->root($root);
+        $feed->creator($host);
+
+        $feed->update_ts();
+
+        $feed->link( unapi => $base) if ($flesh_feed);
+
+        print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
+        print $U->entityize($feed->toString) . "\n";
+
+        return Apache2::Const::OK;
+    }
+
+    my $req = $supercat->request("open-ils.supercat.$type.$format.$command",$id);
+    $req->wait_complete;
+
+    if ($req->failed) {
+        print "Content-type: text/html; charset=utf-8\n\n";
+        $apache->custom_response( 404, <<"        HTML");
+        <html>
+            <head>
+                <title>$type $id not found!</title>
+            </head>
+            <body>
+                <br/>
+                <center>Sorry, we couldn't $command a $type with the id of $id in format $format.</center>
+            </body>
+        </html>
+        HTML
+        return 404;
+    }
+
+    print "Content-type: application/xml; charset=utf-8\n\n";
+    print $U->entityize( $parser->parse_string( $req->gather(1) )->documentElement->toString );
+
+    return Apache2::Const::OK;
 }
 
 
 sub bookbag_feed {
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
-
-       my $cgi = new CGI;
-
-       my $year = (gmtime())[5] + 1900;
-       my $host = $cgi->virtual_host || $cgi->server_name;
-
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
-
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'feed', $url)[0] . '/';
-       my $base = (split 'bookbag', $url)[0] . '/bookbag';
-       my $unapi = (split 'feed', $url)[0] . '/unapi';
-
-       my $skin = $cgi->param('skin') || 'default';
-       my $locale = $cgi->param('locale') || 'en-US';
-       my $org = $cgi->param('searchOrg');
-
-       # Enable localized results of copy status, etc
-       $supercat->session_locale($locale);
-
-       my $org_unit = get_ou($org);
-       my $scope = "l=" . $org_unit->[0]->id . "&";
-
-       $root =~ s{(?<!http:)//}{/}go;
-       $base =~ s{(?<!http:)//}{/}go;
-       $unapi =~ s{(?<!http:)//}{/}go;
-
-       my $path = $cgi->path_info;
-       #warn "URL breakdown: $url -> $root -> $base -> $path -> $unapi";
-
-       my ($id,$type) = reverse split '/', $path;
-       my $flesh_feed = parse_feed_type($type);
-
-       my $bucket = $actor->request("open-ils.actor.container.public.flesh", 'biblio', $id)->gather(1);
-       return Apache2::Const::NOT_FOUND unless($bucket);
-
-       my $bucket_tag = "tag:$host,$year:record_bucket/$id";
-       if ($type eq 'opac') {
-               print "Location: $root/../../$locale/skin/$skin/xml/rresult.xml?$scope" . "rt=list&" .
-                       join('&', map { "rl=" . $_->target_biblio_record_entry } @{ $bucket->items }) .
-                       "\n\n";
-               return 302;
-       }
-
-       my $feed = create_record_feed(
-               'record',
-               $type,
-               [ map { $_->target_biblio_record_entry } @{ $bucket->items } ],
-               $unapi,
-               $org_unit->[0]->shortname,
-               undef,
-               $flesh_feed
-       );
-       $feed->root($root);
-       $feed->id($bucket_tag);
-
-       $feed->title("Items in Book Bag [".$bucket->name."]");
-       $feed->creator($host);
-       $feed->update_ts();
-
-       $feed->link(alternate => $base . "/rss2-full/$id" => 'application/rss+xml');
-       $feed->link(atom => $base . "/atom-full/$id" => 'application/atom+xml');
-       $feed->link(html => $base . "/html-full/$id" => 'text/html');
-       $feed->link(unapi => $unapi);
-
-       $feed->link(
-               OPAC =>
-               "http://$host/opac/$locale/skin/$skin/xml/rresult.xml?$scope" . "rt=list&" .
-                       join('&', map { 'rl=' . $_->target_biblio_record_entry } @{$bucket->items} ),
-               'text/html'
-       );
-
-
-       print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
-       print $U->entityize($feed->toString) . "\n";
-
-       return Apache2::Const::OK;
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
+
+    my $cgi = new CGI;
+
+    my $year = (gmtime())[5] + 1900;
+    my $host = $cgi->virtual_host || $cgi->server_name;
+
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
+
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'feed', $url)[0] . '/';
+    my $base = (split 'bookbag', $url)[0] . '/bookbag';
+    my $unapi = (split 'feed', $url)[0] . '/unapi';
+
+    my $skin = $cgi->param('skin') || 'default';
+    my $locale = $cgi->param('locale') || 'en-US';
+    my $org = $cgi->param('searchOrg');
+
+    # Enable localized results of copy status, etc
+    $supercat->session_locale($locale);
+
+    my $org_unit = get_ou($org);
+    my $scope = "l=" . $org_unit->[0]->id . "&";
+
+    $root =~ s{(?<!http:)//}{/}go;
+    $base =~ s{(?<!http:)//}{/}go;
+    $unapi =~ s{(?<!http:)//}{/}go;
+
+    my $path = $cgi->path_info;
+    #warn "URL breakdown: $url -> $root -> $base -> $path -> $unapi";
+
+    my ($id,$type) = reverse split '/', $path;
+    my $flesh_feed = parse_feed_type($type);
+
+    my $bucket = $actor->request("open-ils.actor.container.public.flesh", 'biblio', $id)->gather(1);
+    return Apache2::Const::NOT_FOUND unless($bucket);
+
+    my $bucket_tag = "tag:$host,$year:record_bucket/$id";
+    if ($type eq 'opac') {
+        print "Location: $root/../../$locale/skin/$skin/xml/rresult.xml?$scope" . "rt=list&" .
+            join('&', map { "rl=" . $_->target_biblio_record_entry } @{ $bucket->items }) .
+            "\n\n";
+        return 302;
+    }
+
+    my $feed = create_record_feed(
+        'record',
+        $type,
+        [ map { $_->target_biblio_record_entry } @{ $bucket->items } ],
+        $unapi,
+        $org_unit->[0]->shortname,
+        undef,
+        $flesh_feed
+    );
+    $feed->root($root);
+    $feed->id($bucket_tag);
+
+    $feed->title("Items in Book Bag [".$bucket->name."]");
+    $feed->creator($host);
+    $feed->update_ts();
+
+    $feed->link(alternate => $base . "/rss2-full/$id" => 'application/rss+xml');
+    $feed->link(atom => $base . "/atom-full/$id" => 'application/atom+xml');
+    $feed->link(html => $base . "/html-full/$id" => 'text/html');
+    $feed->link(unapi => $unapi);
+
+    $feed->link(
+        OPAC =>
+        "http://$host/opac/$locale/skin/$skin/xml/rresult.xml?$scope" . "rt=list&" .
+            join('&', map { 'rl=' . $_->target_biblio_record_entry } @{$bucket->items} ),
+        'text/html'
+    );
+
+
+    print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
+    print $U->entityize($feed->toString) . "\n";
+
+    return Apache2::Const::OK;
 }
 
 sub changes_feed {
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
 
-       my $cgi = new CGI;
+    my $cgi = new CGI;
 
-       my $year = (gmtime())[5] + 1900;
-       my $host = $cgi->virtual_host || $cgi->server_name;
+    my $year = (gmtime())[5] + 1900;
+    my $host = $cgi->virtual_host || $cgi->server_name;
 
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
 
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'feed', $url)[0];
-       my $base = (split 'freshmeat', $url)[0] . '/freshmeat';
-       my $unapi = (split 'feed', $url)[0] . 'unapi';
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'feed', $url)[0];
+    my $base = (split 'freshmeat', $url)[0] . '/freshmeat';
+    my $unapi = (split 'feed', $url)[0] . 'unapi';
 
-       my $skin = $cgi->param('skin') || 'default';
-       my $locale = $cgi->param('locale') || 'en-US';
-       my $org = $cgi->param('searchOrg');
+    my $skin = $cgi->param('skin') || 'default';
+    my $locale = $cgi->param('locale') || 'en-US';
+    my $org = $cgi->param('searchOrg');
 
-       # Enable localized results of copy status, etc
-       $supercat->session_locale($locale);
+    # Enable localized results of copy status, etc
+    $supercat->session_locale($locale);
 
-       my $org_unit = get_ou($org);
-       my $scope = "l=" . $org_unit->[0]->id . "&";
+    my $org_unit = get_ou($org);
+    my $scope = "l=" . $org_unit->[0]->id . "&";
 
-       my $path = $cgi->path_info;
-       #warn "URL breakdown: $url ($rel_name) -> $root -> $base -> $path -> $unapi";
+    my $path = $cgi->path_info;
+    #warn "URL breakdown: $url ($rel_name) -> $root -> $base -> $path -> $unapi";
 
-       $path =~ s/^\/(?:feed\/)?freshmeat\///og;
-       
-       my ($type,$rtype,$axis,$limit,$date) = split '/', $path;
-       my $flesh_feed = parse_feed_type($type);
+    $path =~ s/^\/(?:feed\/)?freshmeat\///og;
+    
+    my ($type,$rtype,$axis,$limit,$date) = split '/', $path;
+    my $flesh_feed = parse_feed_type($type);
 
-       $limit ||= 10;
-       $limit = 10 if $limit !~ /^\d+$/;
+    $limit ||= 10;
+    $limit = 10 if $limit !~ /^\d+$/;
 
-       my $list = $supercat->request("open-ils.supercat.$rtype.record.$axis.recent", $date, $limit)->gather(1);
+    my $list = $supercat->request("open-ils.supercat.$rtype.record.$axis.recent", $date, $limit)->gather(1);
 
-       #if ($type eq 'opac') {
-       #       print "Location: $root/../../en-US/skin/default/xml/rresult.xml?rt=list&" .
-       #               join('&', map { "rl=" . $_ } @$list) .
-       #               "\n\n";
-       #       return 302;
-       #}
+    #if ($type eq 'opac') {
+    #    print "Location: $root/../../en-US/skin/default/xml/rresult.xml?rt=list&" .
+    #        join('&', map { "rl=" . $_ } @$list) .
+    #        "\n\n";
+    #    return 302;
+    #}
 
-       my $search = 'record';
-       if ($rtype eq 'authority') {
-               $search = 'authority';
-       }
-       my $feed = create_record_feed( $search, $type, $list, $unapi, $org_unit->[0]->shortname, undef, $flesh_feed);
-       $feed->root($root);
+    my $search = 'record';
+    if ($rtype eq 'authority') {
+        $search = 'authority';
+    }
+    my $feed = create_record_feed( $search, $type, $list, $unapi, $org_unit->[0]->shortname, undef, $flesh_feed);
+    $feed->root($root);
 
-       if ($date) {
-               $feed->title("Up to $limit recent $rtype ${axis}s from $date forward");
-       } else {
-               $feed->title("$limit most recent $rtype ${axis}s");
-       }
+    if ($date) {
+        $feed->title("Up to $limit recent $rtype ${axis}s from $date forward");
+    } else {
+        $feed->title("$limit most recent $rtype ${axis}s");
+    }
 
-       $feed->creator($host);
-       $feed->update_ts();
+    $feed->creator($host);
+    $feed->update_ts();
 
-       $feed->link(alternate => $base . "/rss2-full/$rtype/$axis/$limit/$date" => 'application/rss+xml');
-       $feed->link(atom => $base . "/atom-full/$rtype/$axis/$limit/$date" => 'application/atom+xml');
-       $feed->link(html => $base . "/html-full/$rtype/$axis/$limit/$date" => 'text/html');
-       $feed->link(unapi => $unapi);
+    $feed->link(alternate => $base . "/rss2-full/$rtype/$axis/$limit/$date" => 'application/rss+xml');
+    $feed->link(atom => $base . "/atom-full/$rtype/$axis/$limit/$date" => 'application/atom+xml');
+    $feed->link(html => $base . "/html-full/$rtype/$axis/$limit/$date" => 'text/html');
+    $feed->link(unapi => $unapi);
 
-       $feed->link(
-               OPAC =>
-               "http://$host/opac/$locale/skin/$skin/xml/rresult.xml?$scope" . "rt=list&" .
-                       join('&', map { 'rl=' . $_} @$list ),
-               'text/html'
-       );
+    $feed->link(
+        OPAC =>
+        "http://$host/opac/$locale/skin/$skin/xml/rresult.xml?$scope" . "rt=list&" .
+            join('&', map { 'rl=' . $_} @$list ),
+        'text/html'
+    );
 
 
-       print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
-       print $U->entityize($feed->toString) . "\n";
+    print "Content-type: ". $feed->type ."; charset=utf-8\n\n";
+    print $U->entityize($feed->toString) . "\n";
 
-       return Apache2::Const::OK;
+    return Apache2::Const::OK;
 }
 
 sub opensearch_osd {
-       my $version = shift;
-       my $lib = shift;
-       my $class = shift;
-       my $base = shift;
+    my $version = shift;
+    my $lib = shift;
+    my $class = shift;
+    my $base = shift;
 
-       if ($version eq '1.0') {
-               print <<OSD;
+    if ($version eq '1.0') {
+        print <<OSD;
 Content-type: application/opensearchdescription+xml; charset=utf-8
 
 <?xml version="1.0" encoding="UTF-8"?>
@@ -1057,8 +1096,8 @@ Content-type: application/opensearchdescription+xml; charset=utf-8
   <AdultContent>false</AdultContent>
 </OpenSearchDescription>
 OSD
-       } else {
-               print <<OSD;
+    } else {
+        print <<OSD;
 Content-type: application/opensearchdescription+xml; charset=utf-8
 
 <?xml version="1.0" encoding="UTF-8"?>
@@ -1089,262 +1128,262 @@ Content-type: application/opensearchdescription+xml; charset=utf-8
   <InputEncoding>UTF-8</InputEncoding>
 </OpenSearchDescription>
 OSD
-       }
+    }
 
-       return Apache2::Const::OK;
+    return Apache2::Const::OK;
 }
 
 sub opensearch_feed {
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
-
-       my $cgi = new CGI;
-       my $year = (gmtime())[5] + 1900;
-
-       my $host = $cgi->virtual_host || $cgi->server_name;
-
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
 
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'opensearch', $url)[0];
-       my $base = (split 'opensearch', $url)[0] . 'opensearch';
-       my $unapi = (split 'opensearch', $url)[0] . 'unapi';
+    my $cgi = new CGI;
+    my $year = (gmtime())[5] + 1900;
 
-       my $path = $cgi->path_info;
-       #warn "URL breakdown: $url ($rel_name) -> $root -> $base -> $path -> $unapi";
-
-       if ($path =~ m{^/?(1\.\d{1})/(?:([^/]+)/)?([^/]+)/osd.xml}o) {
-               
-               my $version = $1;
-               my $lib = uc($2);
-               my $class = $3;
-
-               if (!$lib || $lib eq '-') {
-                       $lib = $actor->request(
-                               'open-ils.actor.org_unit_list.search' => parent_ou => undef
-                       )->gather(1)->[0]->shortname;
-               }
-
-               if ($class eq '-') {
-                       $class = 'keyword';
-               }
-
-               return opensearch_osd($version, $lib, $class, $base);
-       }
-
-
-       my $page = $cgi->param('startPage') || 1;
-       my $offset = $cgi->param('startIndex') || 1;
-       my $limit = $cgi->param('count') || 10;
-
-       $page = 1 if ($page !~ /^\d+$/);
-       $offset = 1 if ($offset !~ /^\d+$/);
-       $limit = 10 if ($limit !~ /^\d+$/); $limit = 25 if ($limit > 25);
-
-       if ($page > 1) {
-               $offset = ($page - 1) * $limit;
-       } else {
-               $offset -= 1;
-       }
-
-       my ($version,$org,$type,$class,$terms,$sort,$sortdir,$lang) = ('','','','','','','','');
-       (undef,$version,$org,$type,$class,$terms,$sort,$sortdir,$lang) = split '/', $path;
-
-       $lang = $cgi->param('searchLang') if $cgi->param('searchLang');
-       $lang = '' if ($lang eq '*');
-
-       $sort = $cgi->param('searchSort') if $cgi->param('searchSort');
-       $sort ||= '';
-       $sortdir = $cgi->param('searchSortDir') if $cgi->param('searchSortDir');
-       $sortdir ||= '';
-
-       $terms .= " " if ($terms && $cgi->param('searchTerms'));
-       $terms .= $cgi->param('searchTerms') if $cgi->param('searchTerms');
-
-       $class = $cgi->param('searchClass') if $cgi->param('searchClass');
-       $class ||= '-';
-
-       $type = $cgi->param('responseType') if $cgi->param('responseType');
-       $type ||= '-';
-
-       $org = $cgi->param('searchOrg') if $cgi->param('searchOrg');
-       $org ||= '-';
-
-
-       my $kwt = $cgi->param('kw');
-       my $tit = $cgi->param('ti');
-       my $aut = $cgi->param('au');
-       my $sut = $cgi->param('su');
-       my $set = $cgi->param('se');
-
-       $terms .= " " if ($terms && $kwt);
-       $terms .= "keyword: $kwt" if ($kwt);
-       $terms .= " " if ($terms && $tit);
-       $terms .= "title: $tit" if ($tit);
-       $terms .= " " if ($terms && $aut);
-       $terms .= "author: $aut" if ($aut);
-       $terms .= " " if ($terms && $sut);
-       $terms .= "subject: $sut" if ($sut);
-       $terms .= " " if ($terms && $set);
-       $terms .= "series: $set" if ($set);
-
-       if ($version eq '1.0') {
-               $type = 'rss2';
-       } elsif ($type eq '-') {
-               $type = 'atom';
-       }
-       my $flesh_feed = parse_feed_type($type);
-
-       $terms = decode_utf8($terms);
-       $lang = 'eng' if ($lang eq 'en-US');
-
-       $log->debug("OpenSearch terms: $terms");
-
-       my $org_unit = get_ou($org);
-
-       # Apostrophes break search and get indexed as spaces anyway
-       my $safe_terms = $terms;
-       $safe_terms =~ s{'}{ }go;
-
-       my $recs = $search->request(
-               'open-ils.search.biblio.multiclass.query' => {
-                       org_unit        => $org_unit->[0]->id,
-                       offset          => $offset,
-                       limit           => $limit,
-                       sort            => $sort,
-                       sort_dir        => $sortdir,
+    my $host = $cgi->virtual_host || $cgi->server_name;
+
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
+
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'opensearch', $url)[0];
+    my $base = (split 'opensearch', $url)[0] . 'opensearch';
+    my $unapi = (split 'opensearch', $url)[0] . 'unapi';
+
+    my $path = $cgi->path_info;
+    #warn "URL breakdown: $url ($rel_name) -> $root -> $base -> $path -> $unapi";
+
+    if ($path =~ m{^/?(1\.\d{1})/(?:([^/]+)/)?([^/]+)/osd.xml}o) {
+        
+        my $version = $1;
+        my $lib = uc($2);
+        my $class = $3;
+
+        if (!$lib || $lib eq '-') {
+             $lib = $actor->request(
+                'open-ils.actor.org_unit_list.search' => parent_ou => undef
+            )->gather(1)->[0]->shortname;
+        }
+
+        if ($class eq '-') {
+            $class = 'keyword';
+        }
+
+        return opensearch_osd($version, $lib, $class, $base);
+    }
+
+
+    my $page = $cgi->param('startPage') || 1;
+    my $offset = $cgi->param('startIndex') || 1;
+    my $limit = $cgi->param('count') || 10;
+
+    $page = 1 if ($page !~ /^\d+$/);
+    $offset = 1 if ($offset !~ /^\d+$/);
+    $limit = 10 if ($limit !~ /^\d+$/); $limit = 25 if ($limit > 25);
+
+    if ($page > 1) {
+        $offset = ($page - 1) * $limit;
+    } else {
+        $offset -= 1;
+    }
+
+    my ($version,$org,$type,$class,$terms,$sort,$sortdir,$lang) = ('','','','','','','','');
+    (undef,$version,$org,$type,$class,$terms,$sort,$sortdir,$lang) = split '/', $path;
+
+    $lang = $cgi->param('searchLang') if $cgi->param('searchLang');
+    $lang = '' if ($lang eq '*');
+
+    $sort = $cgi->param('searchSort') if $cgi->param('searchSort');
+    $sort ||= '';
+    $sortdir = $cgi->param('searchSortDir') if $cgi->param('searchSortDir');
+    $sortdir ||= '';
+
+    $terms .= " " if ($terms && $cgi->param('searchTerms'));
+    $terms .= $cgi->param('searchTerms') if $cgi->param('searchTerms');
+
+    $class = $cgi->param('searchClass') if $cgi->param('searchClass');
+    $class ||= '-';
+
+    $type = $cgi->param('responseType') if $cgi->param('responseType');
+    $type ||= '-';
+
+    $org = $cgi->param('searchOrg') if $cgi->param('searchOrg');
+    $org ||= '-';
+
+
+    my $kwt = $cgi->param('kw');
+    my $tit = $cgi->param('ti');
+    my $aut = $cgi->param('au');
+    my $sut = $cgi->param('su');
+    my $set = $cgi->param('se');
+
+    $terms .= " " if ($terms && $kwt);
+    $terms .= "keyword: $kwt" if ($kwt);
+    $terms .= " " if ($terms && $tit);
+    $terms .= "title: $tit" if ($tit);
+    $terms .= " " if ($terms && $aut);
+    $terms .= "author: $aut" if ($aut);
+    $terms .= " " if ($terms && $sut);
+    $terms .= "subject: $sut" if ($sut);
+    $terms .= " " if ($terms && $set);
+    $terms .= "series: $set" if ($set);
+
+    if ($version eq '1.0') {
+        $type = 'rss2';
+    } elsif ($type eq '-') {
+        $type = 'atom';
+    }
+    my $flesh_feed = parse_feed_type($type);
+
+    $terms = decode_utf8($terms);
+    $lang = 'eng' if ($lang eq 'en-US');
+
+    $log->debug("OpenSearch terms: $terms");
+
+    my $org_unit = get_ou($org);
+
+    # Apostrophes break search and get indexed as spaces anyway
+    my $safe_terms = $terms;
+    $safe_terms =~ s{'}{ }go;
+
+    my $recs = $search->request(
+        'open-ils.search.biblio.multiclass.query' => {
+            org_unit    => $org_unit->[0]->id,
+            offset        => $offset,
+            limit        => $limit,
+            sort        => $sort,
+            sort_dir    => $sortdir,
             default_class => $class,
-                       ($lang ?    ( 'language' => $lang    ) : ()),
-               } => $safe_terms => 1
-       )->gather(1);
-
-       $log->debug("Hits for [$terms]: $recs->{count}");
-
-       my $feed = create_record_feed(
-               'record',
-               $type,
-               [ map { $_->[0] } @{$recs->{ids}} ],
-               $unapi,
-               $org,
-               undef,
-               $flesh_feed
-       );
-
-       $log->debug("Feed created...");
-
-       $feed->root($root);
-       $feed->lib($org);
-       $feed->search($safe_terms);
-       $feed->class($class);
-
-       $feed->title("Search results for [$terms] at ".$org_unit->[0]->name);
-
-       $feed->creator($host);
-       $feed->update_ts();
-
-       $feed->_create_node(
-               $feed->{item_xpath},
-               'http://a9.com/-/spec/opensearch/1.1/',
-               'totalResults',
-               $recs->{count},
-       );
-
-       $feed->_create_node(
-               $feed->{item_xpath},
-               'http://a9.com/-/spec/opensearch/1.1/',
-               'startIndex',
-               $offset + 1,
-       );
-
-       $feed->_create_node(
-               $feed->{item_xpath},
-               'http://a9.com/-/spec/opensearch/1.1/',
-               'itemsPerPage',
-               $limit,
-       );
-
-       $log->debug("...basic feed data added...");
-
-       $feed->link(
-               next =>
-               $base . "/$version/$org/$type/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang&startIndex=" . int($offset + $limit + 1) . "&count=" . $limit =>
-               'application/opensearch+xml'
-       ) if ($offset + $limit < $recs->{count});
-
-       $feed->link(
-               previous =>
-               $base . "/$version/$org/$type/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang&startIndex=" . int(($offset - $limit) + 1) . "&count=" . $limit =>
-               'application/opensearch+xml'
-       ) if ($offset);
-
-       $feed->link(
-               self =>
-               $base .  "/$version/$org/$type/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
-               'application/opensearch+xml'
-       );
-
-       $feed->link(
-               alternate =>
-               $base .  "/$version/$org/rss2-full/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
-               'application/rss+xml'
-       );
-
-       $feed->link(
-               atom =>
-               $base .  "/$version/$org/atom-full/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
-               'application/atom+xml'
-       );
-
-       $feed->link(
-               'html' =>
-               $base .  "/$version/$org/html/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
-               'text/html'
-       );
-
-       $feed->link(
-               'html-full' =>
-               $base .  "/$version/$org/html-full/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
-               'text/html'
-       );
-
-       $feed->link( 'unapi-server' => $unapi);
-
-       $log->debug("...feed links added...");
-
-#      $feed->link(
-#              opac =>
-#              $root . "../$lang/skin/default/xml/rresult.xml?rt=list&" .
-#                      join('&', map { 'rl=' . $_->[0] } grep { ref $_ && defined $_->[0] } @{$recs->{ids}} ),
-#              'text/html'
-#      );
-
-       #print $cgi->header( -type => $feed->type, -charset => 'UTF-8') . entityize($feed->toString) . "\n";
-       print $cgi->header( -type => $feed->type, -charset => 'UTF-8') . $feed->toString . "\n";
-
-       $log->debug("...and feed returned.");
-
-       return Apache2::Const::OK;
+            ($lang ?    ( 'language' => $lang    ) : ()),
+        } => $safe_terms => 1
+    )->gather(1);
+
+    $log->debug("Hits for [$terms]: $recs->{count}");
+
+    my $feed = create_record_feed(
+        'record',
+        $type,
+        [ map { $_->[0] } @{$recs->{ids}} ],
+        $unapi,
+        $org,
+        undef,
+        $flesh_feed
+    );
+
+    $log->debug("Feed created...");
+
+    $feed->root($root);
+    $feed->lib($org);
+    $feed->search($safe_terms);
+    $feed->class($class);
+
+    $feed->title("Search results for [$terms] at ".$org_unit->[0]->name);
+
+    $feed->creator($host);
+    $feed->update_ts();
+
+    $feed->_create_node(
+        $feed->{item_xpath},
+        'http://a9.com/-/spec/opensearch/1.1/',
+        'totalResults',
+        $recs->{count},
+    );
+
+    $feed->_create_node(
+        $feed->{item_xpath},
+        'http://a9.com/-/spec/opensearch/1.1/',
+        'startIndex',
+        $offset + 1,
+    );
+
+    $feed->_create_node(
+        $feed->{item_xpath},
+        'http://a9.com/-/spec/opensearch/1.1/',
+        'itemsPerPage',
+        $limit,
+    );
+
+    $log->debug("...basic feed data added...");
+
+    $feed->link(
+        next =>
+        $base . "/$version/$org/$type/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang&startIndex=" . int($offset + $limit + 1) . "&count=" . $limit =>
+        'application/opensearch+xml'
+    ) if ($offset + $limit < $recs->{count});
+
+    $feed->link(
+        previous =>
+        $base . "/$version/$org/$type/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang&startIndex=" . int(($offset - $limit) + 1) . "&count=" . $limit =>
+        'application/opensearch+xml'
+    ) if ($offset);
+
+    $feed->link(
+        self =>
+        $base .  "/$version/$org/$type/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
+        'application/opensearch+xml'
+    );
+
+    $feed->link(
+        alternate =>
+        $base .  "/$version/$org/rss2-full/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
+        'application/rss+xml'
+    );
+
+    $feed->link(
+        atom =>
+        $base .  "/$version/$org/atom-full/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
+        'application/atom+xml'
+    );
+
+    $feed->link(
+        'html' =>
+        $base .  "/$version/$org/html/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
+        'text/html'
+    );
+
+    $feed->link(
+        'html-full' =>
+        $base .  "/$version/$org/html-full/$class?searchTerms=$terms&searchSort=$sort&searchSortDir=$sortdir&searchLang=$lang" =>
+        'text/html'
+    );
+
+    $feed->link( 'unapi-server' => $unapi);
+
+    $log->debug("...feed links added...");
+
+#    $feed->link(
+#        opac =>
+#        $root . "../$lang/skin/default/xml/rresult.xml?rt=list&" .
+#            join('&', map { 'rl=' . $_->[0] } grep { ref $_ && defined $_->[0] } @{$recs->{ids}} ),
+#        'text/html'
+#    );
+
+    #print $cgi->header( -type => $feed->type, -charset => 'UTF-8') . entityize($feed->toString) . "\n";
+    print $cgi->header( -type => $feed->type, -charset => 'UTF-8') . $feed->toString . "\n";
+
+    $log->debug("...and feed returned.");
+
+    return Apache2::Const::OK;
 }
 
 sub create_record_feed {
-       my $search = shift;
-       my $type = shift;
-       my $records = shift;
-       my $unapi = shift;
+    my $search = shift;
+    my $type = shift;
+    my $records = shift;
+    my $unapi = shift;
 
-       my $lib = uc(shift()) || '-';
-       my $depth = shift;
-       my $flesh = shift;
+    my $lib = uc(shift()) || '-';
+    my $depth = shift;
+    my $flesh = shift;
 
-       my $paging = shift;
+    my $paging = shift;
 
-       my $cgi = new CGI;
-       my $base = $cgi->url;
-       my $host = $cgi->virtual_host || $cgi->server_name;
+    my $cgi = new CGI;
+    my $base = $cgi->url;
+    my $host = $cgi->virtual_host || $cgi->server_name;
 
     my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
     $year += 1900;
@@ -1352,426 +1391,260 @@ sub create_record_feed {
 
     my $tag_prefix = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d", $month, $day);
 
-       my $flesh_feed = defined($flesh) ? $flesh : parse_feed_type($type);
+    my $flesh_feed = defined($flesh) ? $flesh : parse_feed_type($type);
 
-       $type =~ s/(-full|-uris)$//o;
+    $type =~ s/(-full|-uris)$//o;
 
-       my $feed = new OpenILS::WWW::SuperCat::Feed ($type);
-       $feed->base($base) if ($flesh);
-       $feed->unapi($unapi) if ($flesh);
+    my $feed = new OpenILS::WWW::SuperCat::Feed ($type);
+    $feed->base($base) if ($flesh);
+    $feed->unapi($unapi) if ($flesh);
 
-       $type = 'atom' if ($type eq 'html');
-       $type = 'marcxml' if (($type eq 'htmlholdings') || ($type eq 'marctxt') || ($type eq 'ris'));
+    $type = 'atom' if ($type eq 'html');
+    $type = 'marcxml' if (($type eq 'htmlholdings') || ($type eq 'marctxt') || ($type eq 'ris'));
 
-       #$records = $supercat->request( "open-ils.supercat.record.object.retrieve", $records )->gather(1);
+    #$records = $supercat->request( "open-ils.supercat.record.object.retrieve", $records )->gather(1);
 
-       my $count = 0;
-       for my $record (@$records) {
-               next unless($record);
+    my $count = 0;
+    for my $record (@$records) {
+        next unless($record);
 
-               #my $rec = $record->id;
-               my $rec = $record;
+        #my $rec = $record->id;
+        my $rec = $record;
 
-               my $item_tag = "$tag_prefix:biblio-record_entry/$rec/$lib";
-               $item_tag = "$tag_prefix:metabib-metarecord/$rec/$lib" if ($search eq 'metarecord');
-               $item_tag = "$tag_prefix:isbn/$rec/$lib" if ($search eq 'isbn');
-               $item_tag .= "/$depth" if (defined($depth));
+        my $item_tag = "$tag_prefix:biblio-record_entry/$rec/$lib";
+        $item_tag = "$tag_prefix:metabib-metarecord/$rec/$lib" if ($search eq 'metarecord');
+        $item_tag = "$tag_prefix:isbn/$rec/$lib" if ($search eq 'isbn');
+        $item_tag .= "/$depth" if (defined($depth));
 
-               $item_tag = "$tag_prefix:authority-record_entry/$rec" if ($search eq 'authority');
+        $item_tag = "$tag_prefix:authority-record_entry/$rec" if ($search eq 'authority');
 
-               my $xml = $supercat->request(
-                       "open-ils.supercat.$search.$type.retrieve",
-                       $rec
-               )->gather(1);
-               next unless $xml;
+        my $xml = $supercat->request(
+            "open-ils.supercat.$search.$type.retrieve",
+            $rec
+        )->gather(1);
+        next unless $xml;
 
-               my $node = $feed->add_item($xml);
-               next unless $node;
+        my $node = $feed->add_item($xml);
+        next unless $node;
 
-               $xml = '';
-               if ($lib && ($type eq 'marcxml' || $type eq 'atom') &&  $flesh > 0) {
-                       my $r = $supercat->request( "open-ils.supercat.$search.holdings_xml.retrieve", $rec, $lib, $depth, $flesh_feed, $paging );
-                       while ( !$r->complete ) {
-                               $xml .= join('', map {$_->content} $r->recv);
-                       }
-                       $xml .= join('', map {$_->content} $r->recv);
-                       $node->add_holdings($xml);
-               }
+        $xml = '';
+        if ($lib && ($type eq 'marcxml' || $type eq 'atom') &&  $flesh > 0) {
+            my $r = $supercat->request( "open-ils.supercat.$search.holdings_xml.retrieve", $rec, $lib, $depth, $flesh_feed, $paging );
+            while ( !$r->complete ) {
+                $xml .= join('', map {$_->content} $r->recv);
+            }
+            $xml .= join('', map {$_->content} $r->recv);
+            $node->add_holdings($xml);
+        }
 
-               $node->id($item_tag);
-               #$node->update_ts(cleanse_ISO8601($record->edit_date));
-               $node->link(alternate => $feed->unapi . "?id=$item_tag&format=htmlholdings-full" => 'text/html') if ($flesh > 0);
-               $node->link(opac => $feed->unapi . "?id=$item_tag&format=opac") if ($flesh > 0);
-               $node->link(unapi => $feed->unapi . "?id=$item_tag") if ($flesh);
-               $node->link('unapi-id' => $item_tag) if ($flesh);
-       }
+        $node->id($item_tag);
+        #$node->update_ts(cleanse_ISO8601($record->edit_date));
+        $node->link(alternate => $feed->unapi . "?id=$item_tag&format=htmlholdings-full" => 'text/html') if ($flesh > 0);
+        $node->link(opac => $feed->unapi . "?id=$item_tag&format=opac") if ($flesh > 0);
+        $node->link(unapi => $feed->unapi . "?id=$item_tag") if ($flesh);
+        $node->link('unapi-id' => $item_tag) if ($flesh);
+    }
 
-       return $feed;
+    return $feed;
 }
 
 sub string_browse {
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
 
-       my $cgi = new CGI;
-       my $year = (gmtime())[5] + 1900;
+    my $cgi = new CGI;
+    my $year = (gmtime())[5] + 1900;
 
-       my $host = $cgi->virtual_host || $cgi->server_name;
+    my $host = $cgi->virtual_host || $cgi->server_name;
 
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
 
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'browse', $url)[0];
-       my $base = (split 'browse', $url)[0] . 'browse';
-       my $unapi = (split 'browse', $url)[0] . 'unapi';
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'browse', $url)[0];
+    my $base = (split 'browse', $url)[0] . 'browse';
+    my $unapi = (split 'browse', $url)[0] . 'unapi';
 
-       my $path = $cgi->path_info;
-       $path =~ s/^\///og;
+    my $path = $cgi->path_info;
+    $path =~ s/^\///og;
 
-       my ($format,$axis,$site,$string,$page,$page_size) = split '/', $path;
-       #warn " >>> $format -> $axis -> $site -> $string -> $page -> $page_size ";
+    my ($format,$axis,$site,$string,$page,$page_size) = split '/', $path;
+    #warn " >>> $format -> $axis -> $site -> $string -> $page -> $page_size ";
 
     return item_age_browse($apache) if ($axis eq 'item-age'); # short-circut to the item-age sub
 
-       my $status = [$cgi->param('status')];
-       my $cpLoc = [$cgi->param('copyLocation')];
-       $site ||= $cgi->param('searchOrg');
-       $page ||= $cgi->param('startPage') || 0;
-       $page_size ||= $cgi->param('count') || 9;
-
-       $page = 0 if ($page !~ /^-?\d+$/);
-       $page_size = 9 if $page_size !~ /^\d+$/;
-
-       my $prev = join('/', $base,$format,$axis,$site,$string,$page - 1,$page_size);
-       my $next = join('/', $base,$format,$axis,$site,$string,$page + 1,$page_size);
-
-       unless ($string and $axis and grep { $axis eq $_ } keys %browse_types) {
-               warn "something's wrong...";
-               warn " >>> format: $format -> axis: $axis -> site: $site -> string: $string -> page: $page -> page_size: $page_size ";
-               return undef;
-       }
-
-       $string = decode_utf8($string);
-       $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 $status = [$cgi->param('status')];
+    my $cpLoc = [$cgi->param('copyLocation')];
+    $site ||= $cgi->param('searchOrg');
+    $page ||= $cgi->param('startPage') || 0;
+    $page_size ||= $cgi->param('count') || 9;
+
+    $page = 0 if ($page !~ /^-?\d+$/);
+    $page_size = 9 if $page_size !~ /^\d+$/;
+
+    my $prev = join('/', $base,$format,$axis,$site,$string,$page - 1,$page_size);
+    my $next = join('/', $base,$format,$axis,$site,$string,$page + 1,$page_size);
+
+    unless ($string and $axis and grep { $axis eq $_ } keys %browse_types) {
+        warn "something's wrong...";
+        warn " >>> format: $format -> axis: $axis -> site: $site -> string: $string -> page: $page -> page_size: $page_size ";
+        return undef;
+    }
+
+    $string = decode_utf8($string);
+    $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 $norm_format = $format) =~ s/(-full|-uris)$//o;
 
-       my ($header,$content) = $browse_types{$axis}{$norm_format}->($tree,$prev,$next,$format,$unapi,$base,$site);
-       print $header.$content;
-       return Apache2::Const::OK;
+    my ($header,$content) = $browse_types{$axis}{$norm_format}->($tree,$prev,$next,$format,$unapi,$base,$site);
+    print $header.$content;
+    return Apache2::Const::OK;
 }
 
 sub string_startwith {
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
-
-       my $cgi = new CGI;
-       my $year = (gmtime())[5] + 1900;
-
-       my $host = $cgi->virtual_host || $cgi->server_name;
-
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
-
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'startwith', $url)[0];
-       my $base = (split 'startwith', $url)[0] . 'startwith';
-       my $unapi = (split 'startwith', $url)[0] . 'unapi';
-
-       my $path = $cgi->path_info;
-       $path =~ s/^\///og;
-
-       my ($format,$axis,$site,$string,$page,$page_size) = split '/', $path;
-       #warn " >>> $format -> $axis -> $site -> $string -> $page -> $page_size ";
-
-       my $status = [$cgi->param('status')];
-       my $cpLoc = [$cgi->param('copyLocation')];
-       $site ||= $cgi->param('searchOrg');
-       $page ||= $cgi->param('startPage') || 0;
-       $page_size ||= $cgi->param('count') || 9;
-
-       $page = 0 if ($page !~ /^-?\d+$/);
-       $page_size = 9 if $page_size !~ /^\d+$/;
-
-       my $prev = join('/', $base,$format,$axis,$site,$string,$page - 1,$page_size);
-       my $next = join('/', $base,$format,$axis,$site,$string,$page + 1,$page_size);
-
-       unless ($string and $axis and grep { $axis eq $_ } keys %browse_types) {
-               warn "something's wrong...";
-               warn " >>> format: $format -> axis: $axis -> site: $site -> string: $string -> page: $page -> page_size: $page_size ";
-               return undef;
-       }
-
-       $string = decode_utf8($string);
-       $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 $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
+
+    my $cgi = new CGI;
+    my $year = (gmtime())[5] + 1900;
+
+    my $host = $cgi->virtual_host || $cgi->server_name;
+
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
+
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'startwith', $url)[0];
+    my $base = (split 'startwith', $url)[0] . 'startwith';
+    my $unapi = (split 'startwith', $url)[0] . 'unapi';
+
+    my $path = $cgi->path_info;
+    $path =~ s/^\///og;
+
+    my ($format,$axis,$site,$string,$page,$page_size) = split '/', $path;
+    #warn " >>> $format -> $axis -> $site -> $string -> $page -> $page_size ";
+
+    my $status = [$cgi->param('status')];
+    my $cpLoc = [$cgi->param('copyLocation')];
+    $site ||= $cgi->param('searchOrg');
+    $page ||= $cgi->param('startPage') || 0;
+    $page_size ||= $cgi->param('count') || 9;
+
+    $page = 0 if ($page !~ /^-?\d+$/);
+    $page_size = 9 if $page_size !~ /^\d+$/;
+
+    my $prev = join('/', $base,$format,$axis,$site,$string,$page - 1,$page_size);
+    my $next = join('/', $base,$format,$axis,$site,$string,$page + 1,$page_size);
+
+    unless ($string and $axis and grep { $axis eq $_ } keys %browse_types) {
+        warn "something's wrong...";
+        warn " >>> format: $format -> axis: $axis -> site: $site -> string: $string -> page: $page -> page_size: $page_size ";
+        return undef;
+    }
+
+    $string = decode_utf8($string);
+    $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 $norm_format = $format) =~ s/(-full|-uris)$//o;
 
-       my ($header,$content) = $browse_types{$axis}{$norm_format}->($tree,$prev,$next,$format,$unapi,$base,$site);
-       print $header.$content;
-       return Apache2::Const::OK;
+    my ($header,$content) = $browse_types{$axis}{$norm_format}->($tree,$prev,$next,$format,$unapi,$base,$site);
+    print $header.$content;
+    return Apache2::Const::OK;
 }
 
 sub item_age_browse {
-       my $apache = shift;
-       return Apache2::Const::DECLINED if (-e $apache->filename);
-
-       my $cgi = new CGI;
-       my $year = (gmtime())[5] + 1900;
-
-       my $host = $cgi->virtual_host || $cgi->server_name;
-
-       my $add_path = 0;
-       if ( $cgi->server_software !~ m|^Apache/2.2| ) {
-               my $rel_name = $cgi->url(-relative=>1);
-               $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
-       }
-
-       my $url = $cgi->url(-path_info=>$add_path);
-       my $root = (split 'browse', $url)[0];
-       my $base = (split 'browse', $url)[0] . 'browse';
-       my $unapi = (split 'browse', $url)[0] . 'unapi';
-
-       my $path = $cgi->path_info;
-       $path =~ s/^\///og;
-
-       my ($format,$axis,$site,$page,$page_size) = split '/', $path;
-       #warn " >>> $format -> $axis -> $site -> $page -> $page_size ";
-
-       unless ($axis eq 'item-age') {
-               warn "something's wrong...";
-               warn " >>> $format -> $axis -> $site -> $page -> $page_size ";
-               return undef;
-       }
-
-       my $status = [$cgi->param('status')];
-       my $cpLoc = [$cgi->param('copyLocation')];
-       $site ||= $cgi->param('searchOrg') || '-';
-       $page ||= $cgi->param('startPage') || 1;
-       $page_size ||= $cgi->param('count') || 10;
-
-       $page = 1 if ($page !~ /^-?\d+$/ || $page < 1);
-       $page_size = 10 if $page_size !~ /^\d+$/;
-
-       my $prev = join('/', $base,$format,$axis,$site,$page - 1,$page_size);
-       my $next = join('/', $base,$format,$axis,$site,$page + 1,$page_size);
-
-       my $recs = $supercat->request(
-               "open-ils.supercat.new_book_list",
-               $site,
-               $page_size,
-               $page,
-               $status,
-               $cpLoc
-       )->gather(1);
+    my $apache = shift;
+    return Apache2::Const::DECLINED if (-e $apache->filename);
+
+    my $cgi = new CGI;
+    my $year = (gmtime())[5] + 1900;
+
+    my $host = $cgi->virtual_host || $cgi->server_name;
+
+    my $add_path = 0;
+    if ( $cgi->server_software !~ m|^Apache/2.2| ) {
+        my $rel_name = $cgi->url(-relative=>1);
+        $add_path = 1 if ($cgi->url(-path_info=>1) !~ /$rel_name$/);
+    }
+
+    my $url = $cgi->url(-path_info=>$add_path);
+    my $root = (split 'browse', $url)[0];
+    my $base = (split 'browse', $url)[0] . 'browse';
+    my $unapi = (split 'browse', $url)[0] . 'unapi';
+
+    my $path = $cgi->path_info;
+    $path =~ s/^\///og;
+
+    my ($format,$axis,$site,$page,$page_size) = split '/', $path;
+    #warn " >>> $format -> $axis -> $site -> $page -> $page_size ";
+
+    unless ($axis eq 'item-age') {
+        warn "something's wrong...";
+        warn " >>> $format -> $axis -> $site -> $page -> $page_size ";
+        return undef;
+    }
+
+    my $status = [$cgi->param('status')];
+    my $cpLoc = [$cgi->param('copyLocation')];
+    $site ||= $cgi->param('searchOrg') || '-';
+    $page ||= $cgi->param('startPage') || 1;
+    $page_size ||= $cgi->param('count') || 10;
+
+    $page = 1 if ($page !~ /^-?\d+$/ || $page < 1);
+    $page_size = 10 if $page_size !~ /^\d+$/;
+
+    my $prev = join('/', $base,$format,$axis,$site,$page - 1,$page_size);
+    my $next = join('/', $base,$format,$axis,$site,$page + 1,$page_size);
+
+    my $recs = $supercat->request(
+        "open-ils.supercat.new_book_list",
+        $site,
+        $page_size,
+        $page,
+        $status,
+        $cpLoc
+    )->gather(1);
 
     (my $norm_format = $format) =~ s/(-full|-uris)$//o;
 
-       my ($header,$content) = $browse_types{$axis}{$norm_format}->($recs,$prev,$next,$format,$unapi,$base,$site);
-       print $header.$content;
-       return Apache2::Const::OK;
+    my ($header,$content) = $browse_types{$axis}{$norm_format}->($recs,$prev,$next,$format,$unapi,$base,$site);
+    print $header.$content;
+    return Apache2::Const::OK;
 }
 
-our %qualifier_map = (
-
-    # Some EG qualifiers
-    'eg.site'               => 'site',
-    'eg.sort'               => 'sort',
-    'eg.direction'          => 'dir',
-    'eg.available'          => 'available',
-
-    # Title class:
-    'eg.title'              => 'title',
-    'dc.title'              => 'title',
-    'bib.titleabbreviated'  => 'title|abbreviated',
-    'bib.titleuniform'      => 'title|uniform',
-    'bib.titletranslated'   => 'title|translated',
-    'bib.titlealternative'  => 'title',
-    'bib.titleseries'       => 'series',
-    'eg.series'             => 'title',
-
-    # Author/Name class:
-    'eg.author'             => 'author',
-    'eg.name'               => 'author',
-    'creator'               => 'author',
-    'dc.creator'            => 'author',
-    'dc.contributer'        => 'author',
-    'dc.publisher'          => 'keyword',
-    'bib.name'              => 'author',
-    'bib.namepersonal'      => 'author|personal',
-    'bib.namepersonalfamily'=> 'author|personal',
-    'bib.namepersonalgiven' => 'author|personal',
-    'bib.namecorporate'     => 'author|corporate',
-    'bib.nameconference'    => 'author|conference',
-
-    # Subject class:
-    'eg.subject'            => 'subject',
-    'dc.subject'            => 'subject',
-    'bib.subjectplace'      => 'subject|geographic',
-    'bib.subjecttitle'      => 'keyword',
-    'bib.subjectname'       => 'subject|name',
-    'bib.subjectoccupation' => 'keyword',
-
-    # Keyword class:
-    'eg.keyword'            => 'keyword',
-    'srw.serverchoice'      => 'keyword',
-
-    # Identifiers:
-    'dc.identifier'         => 'keyword',
-
-    # Dates:
-    'bib.dateissued'        => undef,
-    'bib.datecreated'       => undef,
-    'bib.datevalid'         => undef,
-    'bib.datemodified'      => undef,
-    'bib.datecopyright'     => undef,
-
-    # Resource Type:
-    'dc.type'               => undef,
-
-    # Format:
-    'dc.format'             => undef,
-
-    # Genre:
-    'bib.genre'             => 'keyword',
-
-    # Target Audience:
-    'bib.audience'          => undef,
-
-    # Place of Origin:
-    'bib.originplace'       => undef,
-
-    # Language
-    'dc.language'           => 'lang',
-
-    # Edition
-    'bib.edition'           => 'keyword',
-
-    # Part:
-    'bib.volume'            => 'keyword',
-    'bib.issue'             => 'keyword',
-    'bib.startpage'         => 'keyword',
-    'bib.endpage'           => 'keyword',
-
-    # Issuance:
-    'bib.issuance'          => 'keyword',
-);
-
 our %qualifier_ids = (
-               eg => 'http://open-ils.org/spec/SRU/context-set/evergreen/v1',
-               dc => 'info:srw/cql-context-set/1/dc-v1.1',
-               bib => 'info:srw/cql-context-set/1/bib-v1.0',
-               srw     => ''
-);
-
-our %nested_qualifier_map = (
-               eg => {
-                       site            => ['site','Evergreen Site Code (shortname)'],
-                       sort            => ['sort','Sort on relevance, title, author, pubdate, create_date or edit_date'],
-                       direction       => ['dir','Sort direction (asc|desc)'],
-                       available       => ['available','Filter to available (true|false)'],
-                       title           => ['title'],
-                       author          => ['author'],
-                       name            => ['author'],
-                       subject         => ['subject'],
-                       keyword         => ['keyword'],
-                       series          => ['series'],
-               },
-               dc => {
-                       title           => ['title'],
-                       creator         => ['author'],
-                       contributor     => ['author'],
-                       publisher       => ['keyword'],
-                       subject         => ['subject'],
-                       identifier      => ['keyword'],
-                       type            => [undef],
-                       format          => [undef],
-                       language        => ['lang'],
-               },
-               bib => {
-               # Title class:
-               titleAbbreviated        => ['title'],
-                   titleUniform                => ['title'],
-                       titleTranslated         => ['title'],
-               titleAlternative        => ['title'],
-                   titleSeries                 => ['series'],
-
-    # Author/Name class:
-                       name                            => ['author'],
-                       namePersonal            => ['author'],
-                       namePersonalFamily      => ['author'],
-                       namePersonalGiven       => ['author'],
-                       nameCorporate           => ['author'],
-                       nameConference          => ['author'],
-
-               # Subject class:
-                       subjectPlace            => ['subject'],
-                       subjectTitle            => ['keyword'],
-                       subjectName                     => ['subject|name'],
-                       subjectOccupation       => ['keyword'],
-
-    # Keyword class:
-
-    # Dates:
-                       dateIssued                      => [undef],
-                       dateCreated                     => [undef],
-                       dateValid                       => [undef],
-                       dateModified            => [undef],
-                       dateCopyright           => [undef],
-
-    # Genre:
-                       genre                           => ['keyword'],
-
-    # Target Audience:
-                       audience                        => [undef],
-
-    # Place of Origin:
-                       originPlace                     => [undef],
-
-    # Edition
-                       edition                         => ['keyword'],
-
-    # Part:
-                       volume                          => ['keyword'],
-                       issue                           => ['keyword'],
-                       startPage                       => ['keyword'],
-                       endPage                         => ['keyword'],
-
-    # Issuance:
-                       issuance                        => ['keyword'],
-               },
-               srw     => {
-                       serverChoice            => ['keyword'],
-               },
+    eg  => 'http://open-ils.org/spec/SRU/context-set/evergreen/v1',
+    dc  => 'info:srw/cql-context-set/1/dc-v1.1',
+    bib => 'info:srw/cql-context-set/1/bib-v1.0',
+    srw => ''
 );
 
 # Our authority search options are currently pretty impoverished;
@@ -1779,59 +1652,59 @@ our %nested_qualifier_map = (
 # ID number
 our %nested_auth_qualifier_map = (
         eg => {
-            id          => ['id', 'Record number'],
-            name        => ['author', 'Personal or corporate author, or meeting name'],
-            title       => ['title', 'Uniform title'],
-            subject     => ['subject', 'Chronological term, topical term, geographic name, or genre/form term'],
-            topic       => ['topic', 'Topical term'],
+            id          => { index => 'id', title => 'Record number'},
+            name        => { index => 'author', title => 'Personal or corporate author, or meeting name'},
+            title       => { index => 'title', title => 'Uniform title'},
+            subject     => { index => 'subject', title => 'Chronological term, topical term, geographic name, or genre/form term'},
+            topic       => { index => 'topic', title => 'Topical term'},
         },
 );
 
 my $base_explain = <<XML;
 <explain
-               id="evergreen-sru-explain-full"
-               authoritative="true"
-               xmlns:z="http://explain.z3950.org/dtd/2.0/"
-               xmlns="http://explain.z3950.org/dtd/2.0/">
-       <serverInfo transport="http" protocol="SRU" version="1.1">
-               <host/>
-               <port/>
-               <database/>
-       </serverInfo>
-
-       <databaseInfo>
-               <title primary="true"/>
-               <description primary="true"/>
-       </databaseInfo>
-
-       <indexInfo>
-               <set identifier="info:srw/cql-context-set/1/cql-v1.2" name="cql"/>
-       </indexInfo>
-
-       <schemaInfo>
-               <schema
-                               identifier="info:srw/schema/1/marcxml-v1.1"
-                               location="http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
-                               sort="true"
-                               retrieve="true"
-                               name="marcxml">
-                       <title>MARC21Slim (marcxml)</title>
-               </schema>
-       </schemaInfo>
-
-       <configInfo>
-               <default type="numberOfRecords">10</default>
-               <default type="contextSet">eg</default>
-               <default type="index">keyword</default>
-               <default type="relation">all</default>
-               <default type="sortSchema">marcxml</default>
-               <default type="retrieveSchema">marcxml</default>
-               <setting type="maximumRecords">50</setting>
-               <supports type="relationModifier">relevant</supports>
-               <supports type="relationModifier">stem</supports>
-               <supports type="relationModifier">fuzzy</supports>
-               <supports type="relationModifier">word</supports>
-       </configInfo>
+        id="evergreen-sru-explain-full"
+        authoritative="true"
+        xmlns:z="http://explain.z3950.org/dtd/2.0/"
+        xmlns="http://explain.z3950.org/dtd/2.0/">
+    <serverInfo transport="http" protocol="SRU" version="1.1">
+        <host/>
+        <port/>
+        <database/>
+    </serverInfo>
+
+    <databaseInfo>
+        <title primary="true"/>
+        <description primary="true"/>
+    </databaseInfo>
+
+    <indexInfo>
+        <set identifier="info:srw/cql-context-set/1/cql-v1.2" name="cql"/>
+    </indexInfo>
+
+    <schemaInfo>
+        <schema
+                identifier="info:srw/schema/1/marcxml-v1.1"
+                location="http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
+                sort="true"
+                retrieve="true"
+                name="marcxml">
+            <title>MARC21Slim (marcxml)</title>
+        </schema>
+    </schemaInfo>
+
+    <configInfo>
+        <default type="numberOfRecords">10</default>
+        <default type="contextSet">eg</default>
+        <default type="index">keyword</default>
+        <default type="relation">all</default>
+        <default type="sortSchema">marcxml</default>
+        <default type="retrieveSchema">marcxml</default>
+        <setting type="maximumRecords">50</setting>
+        <supports type="relationModifier">relevant</supports>
+        <supports type="relationModifier">stem</supports>
+        <supports type="relationModifier">fuzzy</supports>
+        <supports type="relationModifier">word</supports>
+    </configInfo>
 
 </explain>
 XML
@@ -1839,29 +1712,29 @@ XML
 
 my $ex_doc;
 sub sru_search {
-       my $cgi = new CGI;
+    my $cgi = new CGI;
 
-       my $req = SRU::Request->newFromCGI( $cgi );
-       my $resp = SRU::Response->newFromRequest( $req );
+    my $req = SRU::Request->newFromCGI( $cgi );
+    my $resp = SRU::Response->newFromRequest( $req );
 
-       # Find the org_unit shortname, if passed as part of the URL
-       # http://example.com/opac/extras/sru/SHORTNAME
-       my $url = $cgi->path_info;
-       my ($shortname, $holdings) = $url =~ m#/?([^/]*)(/holdings)?#;
+    # Find the org_unit shortname, if passed as part of the URL
+    # http://example.com/opac/extras/sru/SHORTNAME
+    my $url = $cgi->path_info;
+    my ($shortname, $holdings) = $url =~ m#/?([^/]*)(/holdings)?#;
 
-       if ( $resp->type eq 'searchRetrieve' ) {
+    if ( $resp->type eq 'searchRetrieve' ) {
 
-               # Older versions of Debian packages returned terms to us double-encoded,
-               # so we had to forcefully double-decode them a second time with
-               # an outer decode('utf8', $string) call; this seems to be resolved with
-               # Debian Lenny packages sometime between 2009-07-27 and 2010-02-15
-               my $cql_query = decode_utf8($req->query);
-               my $search_string = decode_utf8($req->cql->toEvergreen);
+        # Older versions of Debian packages returned terms to us double-encoded,
+        # so we had to forcefully double-decode them a second time with
+        # an outer decode('utf8', $string) call; this seems to be resolved with
+        # Debian Lenny packages sometime between 2009-07-27 and 2010-02-15
+        my $cql_query = decode_utf8($req->query);
+        my $search_string = decode_utf8($req->cql->toEvergreen);
 
-               # Ensure the search string overrides the default site
-               if ($shortname and $search_string !~ m#site:#) {
-                       $search_string .= " site:$shortname";
-               }
+        # Ensure the search string overrides the default site
+        if ($shortname and $search_string !~ m#site:#) {
+            $search_string .= " site:$shortname";
+        }
 
         my $offset = $req->startRecord;
         $offset-- if ($offset);
@@ -1872,85 +1745,85 @@ sub sru_search {
 
         $log->info("SRU search string [$cql_query] converted to [$search_string]\n");
 
-               my $recs = $search->request(
-                       'open-ils.search.biblio.multiclass.query' => {offset => $offset, limit => $limit} => $search_string => 1
-               )->gather(1);
-
-               my $bre = $supercat->request( 'open-ils.supercat.record.object.retrieve' => [ map { $_->[0] } @{$recs->{ids}} ] )->gather(1);
-
-               foreach my $record (@$bre) {
-                       my $marcxml = $record->marc;
-                       # Make the beast conform to a VDX-supported format
-                       # See http://vdxipedia.oclc.org/index.php/Holdings_Parsing
-                       # Trying to implement LIBSOL_852_A format; so much for standards
-                       if ($holdings) {
-                               my $bib_holdings = $supercat->request('open-ils.supercat.record.basic_holdings.retrieve', $record->id, $shortname || '-')->gather(1);
-                               my $marc = MARC::Record->new_from_xml($marcxml, 'UTF8', 'XML');
-
-                               # Force record leader to 'a' as our data is always UTF8
-                               # Avoids marc8_to_utf8 from being invoked with horrible results
-                               # on the off-chance the record leader isn't correct
-                               my $ldr = $marc->leader;
-                               substr($ldr, 9, 1, 'a');
-                               $marc->leader($ldr);
-
-                               # Expects the record ID in the 001
-                               $marc->delete_field($_) for ($marc->field('001'));
-                               if (!$marc->field('001')) {
-                                       $marc->insert_fields_ordered(
-                                               MARC::Field->new( '001', $record->id )
-                                       );
-                               }
-                               $marc->delete_field($_) for ($marc->field('852')); # remove any legacy 852s
-                               foreach my $cn (keys %$bib_holdings) {
-                                       foreach my $cp (@{$bib_holdings->{$cn}->{'copies'}}) {
-                                               $marc->insert_fields_ordered(
-                                                       MARC::Field->new(
-                                                               '852', '4', '',
-                                                               a => $cp->{'location'},
-                                                               b => $bib_holdings->{$cn}->{'owning_lib'},
-                                                               c => $cn,
-                                                               d => $cp->{'circlib'},
-                                                               g => $cp->{'barcode'},
-                                                               n => $cp->{'status'},
-                                                       )
-                                               );
-                                       }
-                               }
-
-                               # Ensure the data is encoded as UTF8 before we hand it off
-                               $marcxml = encode_utf8($marc->as_xml_record());
-                               $marcxml =~ s/^<\?xml version="1.0" encoding="UTF-8"\?>//o;
-
-                       }
-                       $resp->addRecord(
-                               SRU::Response::Record->new(
-                                       recordSchema    => 'info:srw/schema/1/marcxml-v1.1',
-                                       recordData => $marcxml,
-                                       recordPosition => ++$offset
-                               )
-                       );
-               }
-
-               $resp->numberOfRecords($recs->{count});
-
-       } elsif ( $resp->type eq 'explain' ) {
-               return_sru_explain($cgi, $req, $resp, \$ex_doc,
-                       \%OpenILS::WWW::SuperCat::nested_qualifier_map,
-                       \%OpenILS::WWW::SuperCat::qualifier_ids
-               );
-
-               $resp->record(
-                       SRU::Response::Record->new(
-                               recordSchema    => 'info:srw/cql-context-set/2/zeerex-1.1',
-                               recordData              => $ex_doc
-                       )
-               );
-       }
-
-       print $cgi->header( -type => 'application/xml' );
-       print $U->entityize($resp->asXML) . "\n";
-       return Apache2::Const::OK;
+         my $recs = $search->request(
+            'open-ils.search.biblio.multiclass.query' => {offset => $offset, limit => $limit} => $search_string => 1
+        )->gather(1);
+
+        my $bre = $supercat->request( 'open-ils.supercat.record.object.retrieve' => [ map { $_->[0] } @{$recs->{ids}} ] )->gather(1);
+
+        foreach my $record (@$bre) {
+            my $marcxml = $record->marc;
+            # Make the beast conform to a VDX-supported format
+            # See http://vdxipedia.oclc.org/index.php/Holdings_Parsing
+            # Trying to implement LIBSOL_852_A format; so much for standards
+            if ($holdings) {
+                my $bib_holdings = $supercat->request('open-ils.supercat.record.basic_holdings.retrieve', $record->id, $shortname || '-')->gather(1);
+                my $marc = MARC::Record->new_from_xml($marcxml, 'UTF8', 'XML');
+
+                # Force record leader to 'a' as our data is always UTF8
+                # Avoids marc8_to_utf8 from being invoked with horrible results
+                # on the off-chance the record leader isn't correct
+                my $ldr = $marc->leader;
+                substr($ldr, 9, 1, 'a');
+                $marc->leader($ldr);
+
+                # Expects the record ID in the 001
+                $marc->delete_field($_) for ($marc->field('001'));
+                if (!$marc->field('001')) {
+                    $marc->insert_fields_ordered(
+                        MARC::Field->new( '001', $record->id )
+                    );
+                }
+                $marc->delete_field($_) for ($marc->field('852')); # remove any legacy 852s
+                foreach my $cn (keys %$bib_holdings) {
+                    foreach my $cp (@{$bib_holdings->{$cn}->{'copies'}}) {
+                        $marc->insert_fields_ordered(
+                            MARC::Field->new(
+                                '852', '4', '',
+                                a => $cp->{'location'},
+                                b => $bib_holdings->{$cn}->{'owning_lib'},
+                                c => $cn,
+                                d => $cp->{'circlib'},
+                                g => $cp->{'barcode'},
+                                n => $cp->{'status'},
+                            )
+                        );
+                    }
+                }
+
+                # Ensure the data is encoded as UTF8 before we hand it off
+                $marcxml = encode_utf8($marc->as_xml_record());
+                $marcxml =~ s/^<\?xml version="1.0" encoding="UTF-8"\?>//o;
+
+            }
+            $resp->addRecord(
+                SRU::Response::Record->new(
+                    recordSchema    => 'info:srw/schema/1/marcxml-v1.1',
+                    recordData => $marcxml,
+                    recordPosition => ++$offset
+                )
+            );
+        }
+
+        $resp->numberOfRecords($recs->{count});
+
+    } elsif ( $resp->type eq 'explain' ) {
+        return_sru_explain($cgi, $req, $resp, \$ex_doc,
+            undef,
+            \%OpenILS::WWW::SuperCat::qualifier_ids
+        );
+
+        $resp->record(
+            SRU::Response::Record->new(
+                recordSchema    => 'info:srw/cql-context-set/2/zeerex-1.1',
+                recordData        => $ex_doc
+            )
+        );
+    }
+
+    print $cgi->header( -type => 'application/xml' );
+    print $U->entityize($resp->asXML) . "\n";
+    return Apache2::Const::OK;
 }
 
 
@@ -1986,10 +1859,9 @@ sub sru_search {
         if ( $qualifier ) {
             my ($qset, $qname) = split(/\./, $qualifier);
 
-            $log->debug("SRU toEvergreen: $qset, $qname   $OpenILS::WWW::SuperCat::nested_qualifier_map{$qset}{$qname}[0]\n");
-
-            if ( exists($OpenILS::WWW::SuperCat::nested_qualifier_map{$qset}{$qname}) ) {
-                $qualifier = $OpenILS::WWW::SuperCat::nested_qualifier_map{$qset}{$qname}[0] || 'kw';
+            if ( exists($qualifier_map{$qset}{$qname}) ) {
+                $qualifier = $qualifier_map{$qset}{$qname}{'index'} || 'kw';
+                $log->debug("SRU toEvergreen: $qset, $qname   $qualifier_map{$qset}{$qname}{'index'}\n");
             }
 
             my @modifiers = $relation->getModifiers();
@@ -2030,10 +1902,9 @@ sub sru_search {
         if ( $qualifier ) {
             my ($qset, $qname) = split(/\./, $qualifier);
 
-            $log->debug("SRU toEvergreenAuth: $qset, $qname   $OpenILS::WWW::SuperCat::nested_auth_qualifier_map{$qset}{$qname}[0]\n");
-
             if ( exists($OpenILS::WWW::SuperCat::nested_auth_qualifier_map{$qset}{$qname}) ) {
-                $qualifier = $OpenILS::WWW::SuperCat::nested_auth_qualifier_map{$qset}{$qname}[0] || 'author';
+                $qualifier = $OpenILS::WWW::SuperCat::nested_auth_qualifier_map{$qset}{$qname}{'index'} || 'author';
+                $log->debug("SRU toEvergreenAuth: $qset, $qname   $OpenILS::WWW::SuperCat::nested_auth_qualifier_map{$qset}{$qname}{'index'}\n");
             }
         }
         return { qualifier => $qualifier, term => $term };
@@ -2085,11 +1956,12 @@ sub explain_header {
 }
 
 sub return_sru_explain {
-    my ($cgi, $req, $resp, $explain, $qualifier_map, $qualifier_ids) = @_;
+    my ($cgi, $req, $resp, $explain, $index_map, $qualifier_ids) = @_;
 
+    $index_map ||= \%qualifier_map;
     if (!$$explain) {
         my ($doc, $e) = explain_header($cgi);
-        for my $name ( keys %$qualifier_map ) {
+        for my $name ( keys %{$index_map} ) {
 
             my $identifier = $qualifier_ids->{ $name };
 
@@ -2100,10 +1972,7 @@ sub return_sru_explain {
             $set_node->setAttribute( name => $name );
 
             $e->findnodes('/z:explain/z:indexInfo')->shift->appendChild( $set_node );
-
-            for my $index ( keys %{ $qualifier_map->{$name} } ) {
-                my $desc = $qualifier_map->{$name}{$index}[1] || $index;
-
+            for my $index ( sort keys %{$index_map->{$name}} ) {
                 my $name_node = $doc->createElementNS( 'http://explain.z3950.org/dtd/2.0/', 'name' );
 
                 my $map_node = $doc->createElementNS( 'http://explain.z3950.org/dtd/2.0/', 'map' );
@@ -2115,10 +1984,10 @@ sub return_sru_explain {
                 $index_node->appendChild( $title_node );
                 $index_node->appendChild( $map_node );
 
-                $index_node->setAttribute( id => $name . '.' . $index );
-                $title_node->appendText( $desc );
+                $index_node->setAttribute( id => "$name.$index" );
+                $title_node->appendText($index_map->{$name}{$index}{'title'});
                 $name_node->setAttribute( set => $name );
-                $name_node->appendText($index );
+                $name_node->appendText($index_map->{$name}{$index}{'index'});
 
                 $e->findnodes('/z:explain/z:indexInfo')->shift->appendChild( $index_node );
             }
@@ -2189,24 +2058,24 @@ Returns an aou object for a given actor.org_unit shortname or ID.
 =cut
 
 sub get_ou {
-       my $org = shift || '-';
-       my $org_unit;
-
-       if ($org eq '-') {
-               $org_unit = $actor->request(
-                       'open-ils.actor.org_unit_list.search' => parent_ou => undef
-               )->gather(1);
-       } elsif ($org !~ /^\d+$/o) {
-               $org_unit = $actor->request(
-                       'open-ils.actor.org_unit_list.search' => shortname => uc($org)
-               )->gather(1);
-       } else {
-               $org_unit = $actor->request(
-                       'open-ils.actor.org_unit_list.search' => id => $org
-               )->gather(1);
-       }
-
-       return $org_unit;
+    my $org = shift || '-';
+    my $org_unit;
+
+    if ($org eq '-') {
+         $org_unit = $actor->request(
+            'open-ils.actor.org_unit_list.search' => parent_ou => undef
+        )->gather(1);
+    } elsif ($org !~ /^\d+$/o) {
+         $org_unit = $actor->request(
+            'open-ils.actor.org_unit_list.search' => shortname => uc($org)
+        )->gather(1);
+    } else {
+         $org_unit = $actor->request(
+            'open-ils.actor.org_unit_list.search' => id => $org
+        )->gather(1);
+    }
+
+    return $org_unit;
 }
 
 1;
index ee70d36..daff4d7 100644 (file)
@@ -70,7 +70,7 @@ CREATE TABLE config.upgrade_log (
     install_date    TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
 );
 
-INSERT INTO config.upgrade_log (version) VALUES ('0493'); -- dbs
+INSERT INTO config.upgrade_log (version) VALUES ('0497'); -- miker for tsbere
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
@@ -566,38 +566,6 @@ CREATE TABLE config.remote_account (
     last_activity TIMESTAMP WITH TIME ZONE
 );
 
-CREATE TABLE config.audience_map (
-       code            TEXT    PRIMARY KEY,
-       value           TEXT    NOT NULL,
-       description     TEXT
-);
-
-CREATE TABLE config.lit_form_map (
-       code            TEXT    PRIMARY KEY,
-       value           TEXT    NOT NULL,
-       description     TEXT
-);
-
-CREATE TABLE config.language_map (
-       code    TEXT    PRIMARY KEY,
-       value   TEXT    NOT NULL
-);
-
-CREATE TABLE config.item_form_map (
-       code    TEXT    PRIMARY KEY,
-       value   TEXT    NOT NULL
-);
-
-CREATE TABLE config.item_type_map (
-       code    TEXT    PRIMARY KEY,
-       value   TEXT    NOT NULL
-);
-
-CREATE TABLE config.bib_level_map (
-       code    TEXT    PRIMARY KEY,
-       value   TEXT    NOT NULL
-);
-
 CREATE TABLE config.marc21_rec_type_map (
     code        TEXT    PRIMARY KEY,
     type_val    TEXT    NOT NULL,
@@ -675,7 +643,7 @@ CREATE TABLE config.z3950_attr (
 
 CREATE TABLE config.i18n_locale (
     code        TEXT    PRIMARY KEY,
-    marc_code   TEXT    NOT NULL REFERENCES config.language_map (code) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    marc_code   TEXT    NOT NULL, -- should exist in config.coded_value_map WHERE ctype = 'item_lang'
     name        TEXT    UNIQUE NOT NULL,
     description TEXT
 );
@@ -794,6 +762,63 @@ CREATE OR REPLACE FUNCTION public.split_date_range( TEXT ) RETURNS TEXT AS $func
         SELECT REGEXP_REPLACE( $1, E'(\\d{4})-(\\d{4})', E'\\1 \\2', 'g' );
 $func$ LANGUAGE SQL STRICT IMMUTABLE;
 
+CREATE OR REPLACE FUNCTION public.approximate_date( TEXT, TEXT ) RETURNS TEXT AS $func$
+        SELECT REGEXP_REPLACE( $1, E'\\D', $2, 'g' );
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.approximate_low_date( TEXT ) RETURNS TEXT AS $func$
+        SELECT approximate_date( $1, '0');
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.approximate_high_date( TEXT ) RETURNS TEXT AS $func$
+        SELECT approximate_date( $1, '9');
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.content_or_null( TEXT ) RETURNS TEXT AS $func$
+        SELECT CASE WHEN $1 ~ E'^\\s*$' THEN NULL ELSE $1 END
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.integer_or_null( TEXT ) RETURNS TEXT AS $func$
+        SELECT CASE WHEN $1 ~ E'^\\d+$' THEN $1 ELSE NULL END
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.force_to_isbn13( TEXT ) RETURNS TEXT AS $func$
+    use Business::ISBN;
+    use strict;
+    use warnings;
+
+    # Find the first ISBN, force it to ISBN13 and return it
+
+    my $input = shift;
+
+    foreach my $word (split(/\s/, $input)) {
+        my $isbn = Business::ISBN->new($word);
+
+        # First check the checksum; if it is not valid, fix it and add the original
+        # bad-checksum ISBN to the output
+        if ($isbn && $isbn->is_valid_checksum() == Business::ISBN::BAD_CHECKSUM) {
+            $isbn->fix_checksum();
+        }
+
+        # If we now have a valid ISBN, force it to ISBN13 and return it
+        return $isbn->as_isbn13->isbn if ($isbn && $isbn->is_valid());
+    }
+    return undef;
+$func$ LANGUAGE PLPERLU;
+
+COMMENT ON FUNCTION public.force_to_isbn13(TEXT) IS $$
+/*
+ * Copyright (C) 2011 Equinox Software
+ * Mike Rylander <mrylander@gmail.com>
+ *
+ * Inspired by translate_isbn1013
+ *
+ * The force_to_isbn13 function takes an input ISBN and returns the ISBN13
+ * version without hypens and with a repaired checksum if the checksum was bad
+ */
+$$;
+
+
 CREATE OR REPLACE FUNCTION public.translate_isbn1013( TEXT ) RETURNS TEXT AS $func$
     use Business::ISBN;
     use strict;
@@ -866,6 +891,57 @@ CREATE TABLE config.metabib_field_index_norm_map (
         pos     INT     NOT NULL DEFAULT 0
 );
 
+CREATE TABLE config.record_attr_definition (
+    name        TEXT    PRIMARY KEY,
+    label       TEXT    NOT NULL, -- I18N
+    description TEXT,
+    filter      BOOL    NOT NULL DEFAULT TRUE,  -- becomes QP filter if true
+    sorter      BOOL    NOT NULL DEFAULT FALSE, -- becomes QP sort() axis if true
+
+-- For pre-extracted fields. Takes the first occurance, uses naive subfield ordering
+    tag         TEXT, -- LIKE format
+    sf_list     TEXT, -- pile-o-values, like 'abcd' for a and b and c and d
+
+-- This is used for both tag/sf and xpath entries
+    joiner      TEXT,
+
+-- For xpath-extracted attrs
+    xpath       TEXT,
+    format      TEXT    REFERENCES config.xml_transform (name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    start_pos   INT,
+    string_len  INT,
+
+-- For fixed fields
+    fixed_field TEXT, -- should exist in config.marc21_ff_pos_map.fixed_field
+
+-- For phys-char fields
+    phys_char_sf    INT REFERENCES config.marc21_physical_characteristic_subfield_map (id)
+);
+
+CREATE TABLE config.record_attr_index_norm_map (
+    id      SERIAL  PRIMARY KEY,
+    attr    TEXT    NOT NULL REFERENCES config.record_attr_definition (name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    norm    INT     NOT NULL REFERENCES config.index_normalizer (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    params  TEXT,
+    pos     INT     NOT NULL DEFAULT 0
+);
+
+CREATE TABLE config.coded_value_map (
+    id          SERIAL  PRIMARY KEY,
+    ctype       TEXT    NOT NULL REFERENCES config.record_attr_definition (name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    code        TEXT    NOT NULL,
+    value       TEXT    NOT NULL,
+    description TEXT
+);
+
+CREATE VIEW config.language_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'item_lang';
+CREATE VIEW config.bib_level_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'bib_level';
+CREATE VIEW config.item_form_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'item_form';
+CREATE VIEW config.item_type_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'item_type';
+CREATE VIEW config.lit_form_map AS SELECT code, value, description FROM config.coded_value_map WHERE ctype = 'lit_form';
+CREATE VIEW config.audience_map AS SELECT code, value, description FROM config.coded_value_map WHERE ctype = 'audience';
+CREATE VIEW config.videorecording_format_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'vr_format';
+
 CREATE OR REPLACE FUNCTION oils_tsearch2 () RETURNS TRIGGER AS $$
 DECLARE
     normalizer      RECORD;
index 4064d06..eaa48e9 100644 (file)
@@ -135,41 +135,38 @@ CREATE INDEX metabib_facet_entry_field_idx ON metabib.facet_entry (field);
 CREATE INDEX metabib_facet_entry_value_idx ON metabib.facet_entry (SUBSTRING(value,1,1024));
 CREATE INDEX metabib_facet_entry_source_idx ON metabib.facet_entry (source);
 
-
-CREATE TABLE metabib.rec_descriptor (
-       id              BIGSERIAL PRIMARY KEY,
-       record          BIGINT,
-       item_type       TEXT,
-       item_form       TEXT,
-       bib_level       TEXT,
-       control_type    TEXT,
-       char_encoding   TEXT,
-       enc_level       TEXT,
-       audience        TEXT,
-       lit_form        TEXT,
-       type_mat        TEXT,
-       cat_form        TEXT,
-       pub_status      TEXT,
-       item_lang       TEXT,
-       vr_format       TEXT,
-       date1           TEXT,
-       date2           TEXT
+CREATE TABLE metabib.record_attr (
+       id              BIGINT  PRIMARY KEY REFERENCES biblio.record_entry (id) ON DELETE CASCADE,
+       attrs   HSTORE  NOT NULL DEFAULT ''::HSTORE
+);
+CREATE INDEX metabib_svf_attrs_idx ON metabib.record_attr USING GIST (attrs);
+CREATE INDEX metabib_svf_date1_idx ON metabib.record_attr ((attrs->'date1'));
+CREATE INDEX metabib_svf_dates_idx ON metabib.record_attr ((attrs->'date1'),(attrs->'date2'));
+
+-- Back-compat view ... we're moving to an HSTORE world
+CREATE TYPE metabib.rec_desc_type AS (
+    item_type       TEXT,
+    item_form       TEXT,
+    bib_level       TEXT,
+    control_type    TEXT,
+    char_encoding   TEXT,
+    enc_level       TEXT,
+    audience        TEXT,
+    lit_form        TEXT,
+    type_mat        TEXT,
+    cat_form        TEXT,
+    pub_status      TEXT,
+    item_lang       TEXT,
+    vr_format       TEXT,
+    date1           TEXT,
+    date2           TEXT
 );
-CREATE INDEX metabib_rec_descriptor_record_idx ON metabib.rec_descriptor (record);
-CREATE INDEX metabib_rec_descriptor_item_type_idx ON metabib.rec_descriptor (item_type);
-CREATE INDEX metabib_rec_descriptor_item_form_idx ON metabib.rec_descriptor (item_form);
-CREATE INDEX metabib_rec_descriptor_bib_level_idx ON metabib.rec_descriptor (bib_level);
-CREATE INDEX metabib_rec_descriptor_control_type_idx ON metabib.rec_descriptor (control_type);
-CREATE INDEX metabib_rec_descriptor_char_encoding_idx ON metabib.rec_descriptor (char_encoding);
-CREATE INDEX metabib_rec_descriptor_enc_level_idx ON metabib.rec_descriptor (enc_level);
-CREATE INDEX metabib_rec_descriptor_audience_idx ON metabib.rec_descriptor (audience);
-CREATE INDEX metabib_rec_descriptor_lit_form_idx ON metabib.rec_descriptor (lit_form);
-CREATE INDEX metabib_rec_descriptor_cat_form_idx ON metabib.rec_descriptor (cat_form);
-CREATE INDEX metabib_rec_descriptor_pub_status_idx ON metabib.rec_descriptor (pub_status);
-CREATE INDEX metabib_rec_descriptor_item_lang_idx ON metabib.rec_descriptor (item_lang);
-CREATE INDEX metabib_rec_descriptor_vr_format_idx ON metabib.rec_descriptor (vr_format);
-CREATE INDEX metabib_rec_descriptor_date1_idx ON metabib.rec_descriptor (date1);
-CREATE INDEX metabib_rec_descriptor_dates_idx ON metabib.rec_descriptor (date1,date2);
+
+CREATE VIEW metabib.rec_descriptor AS
+    SELECT  id,
+            id AS record,
+            (populate_record(NULL::metabib.rec_desc_type, attrs)).*
+      FROM  metabib.record_attr;
 
 -- Use a sequence that matches previous version, for easier upgrading.
 CREATE SEQUENCE metabib.full_rec_id_seq;
@@ -459,18 +456,18 @@ return undef;
 
 $func$ LANGUAGE PLPERLU;
 
-CREATE OR REPLACE FUNCTION biblio.marc21_record_type( rid BIGINT ) RETURNS config.marc21_rec_type_map AS $func$
+CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
 DECLARE
-       ldr         RECORD;
+       ldr         TEXT;
        tval        TEXT;
        tval_rec    RECORD;
        bval        TEXT;
        bval_rec    RECORD;
     retval      config.marc21_rec_type_map%ROWTYPE;
 BEGIN
-    SELECT * INTO ldr FROM metabib.full_rec WHERE record = rid AND tag = 'LDR' LIMIT 1;
+    ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
 
-    IF ldr.id IS NULL THEN
+    IF ldr IS NULL OR ldr = '' THEN
         SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
         RETURN retval;
     END IF;
@@ -479,10 +476,10 @@ BEGIN
     SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
 
 
-    tval := SUBSTRING( ldr.value, tval_rec.start_pos + 1, tval_rec.length );
-    bval := SUBSTRING( ldr.value, bval_rec.start_pos + 1, bval_rec.length );
+    tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
+    bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
 
-    -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr.value;
+    -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
 
     SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
 
@@ -495,16 +492,20 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-CREATE OR REPLACE FUNCTION biblio.marc21_extract_fixed_field( rid BIGINT, ff TEXT ) RETURNS TEXT AS $func$
+CREATE OR REPLACE FUNCTION biblio.marc21_record_type( rid BIGINT ) RETURNS config.marc21_rec_type_map AS $func$
+    SELECT * FROM vandelay.marc21_record_type( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
 DECLARE
     rtype       TEXT;
     ff_pos      RECORD;
     tag_data    RECORD;
     val         TEXT;
 BEGIN
-    rtype := (biblio.marc21_record_type( rid )).code;
+    rtype := (vandelay.marc21_record_type( marc )).code;
     FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
-        FOR tag_data IN SELECT * FROM metabib.full_rec WHERE tag = UPPER(ff_pos.tag) AND record = rid LOOP
+        FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
             val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
             RETURN val;
         END LOOP;
@@ -516,30 +517,64 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_fixed_field( rid BIGINT, ff TEXT ) RETURNS TEXT AS $func$
+    SELECT * FROM vandelay.marc21_extract_fixed_field( (SELECT marc FROM biblio.record_entry WHERE id = $1), $2 );
+$func$ LANGUAGE SQL;
+
+CREATE TYPE biblio.record_ff_map AS (record BIGINT, ff_name TEXT, ff_value TEXT);
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
+DECLARE
+    tag_data    TEXT;
+    rtype       TEXT;
+    ff_pos      RECORD;
+    output      biblio.record_ff_map%ROWTYPE;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
+        output.ff_name  := ff_pos.fixed_field;
+        output.ff_value := NULL;
+
+        FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(tag) || '"]/text()', marc ) ) x(value) LOOP
+            output.ff_value := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+            IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
+            RETURN NEXT output;
+            output.ff_value := NULL;
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_all_fixed_fields( rid BIGINT ) RETURNS SETOF biblio.record_ff_map AS $func$
+    SELECT $1 AS record, ff_name, ff_value FROM vandelay.marc21_extract_all_fixed_fields( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
+$func$ LANGUAGE SQL;
+
 CREATE TYPE biblio.marc21_physical_characteristics AS ( id INT, record BIGINT, ptype TEXT, subfield INT, value INT );
-CREATE OR REPLACE FUNCTION biblio.marc21_physical_characteristics( rid BIGINT ) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
 DECLARE
     rowid   INT := 0;
-    _007    RECORD;
+    _007    TEXT;
     ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
     psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
     pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
     retval  biblio.marc21_physical_characteristics%ROWTYPE;
 BEGIN
 
-    SELECT * INTO _007 FROM metabib.full_rec WHERE record = rid AND tag = '007' LIMIT 1;
+    _007 := oils_xpath_string( '//*[@tag="007"]', marc );
 
-    IF _007.id IS NOT NULL THEN
-        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007.value, 1, 1 );
+    IF _007 IS NOT NULL AND _007 <> '' THEN
+        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
 
         IF ptype.ptype_key IS NOT NULL THEN
             FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
-                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007.value, psf.start_pos + 1, psf.length );
+                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
 
                 IF pval.id IS NOT NULL THEN
                     rowid := rowid + 1;
                     retval.id := rowid;
-                    retval.record := rid;
                     retval.ptype := ptype.ptype_key;
                     retval.subfield := psf.id;
                     retval.value := pval.id;
@@ -554,6 +589,10 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION biblio.marc21_physical_characteristics( rid BIGINT ) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+    SELECT id, $1 AS record, ptype, subfield, value FROM vandelay.marc21_physical_characteristics( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
+$func$ LANGUAGE SQL;
+
 CREATE OR REPLACE FUNCTION biblio.extract_quality ( marc TEXT, best_lang TEXT, best_type TEXT ) RETURNS INT AS $func$
 DECLARE
     qual        INT;
@@ -705,37 +744,6 @@ BEGIN
 END;
 $func$ LANGUAGE PLPGSQL;
 
-CREATE OR REPLACE FUNCTION metabib.reingest_metabib_rec_descriptor( bib_id BIGINT ) RETURNS VOID AS $func$
-BEGIN
-    PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
-    IF NOT FOUND THEN
-        DELETE FROM metabib.rec_descriptor WHERE record = bib_id;
-    END IF;
-    INSERT INTO metabib.rec_descriptor (record, item_type, item_form, bib_level, control_type, enc_level, audience, lit_form, type_mat, cat_form, pub_status, item_lang, vr_format, date1, date2)
-        SELECT  bib_id,
-                biblio.marc21_extract_fixed_field( bib_id, 'Type' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'Form' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'BLvl' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'Ctrl' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'ELvl' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'Audn' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'LitF' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'TMat' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'Desc' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'DtSt' ),
-                biblio.marc21_extract_fixed_field( bib_id, 'Lang' ),
-                (   SELECT  v.value
-                      FROM  biblio.marc21_physical_characteristics( bib_id) p
-                            JOIN config.marc21_physical_characteristic_subfield_map s ON (s.id = p.subfield)
-                            JOIN config.marc21_physical_characteristic_value_map v ON (v.id = p.value)
-                      WHERE p.ptype = 'v' AND s.subfield = 'e'    ),
-                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date1'), ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
-                LPAD(NULLIF(REGEXP_REPLACE(NULLIF(biblio.marc21_extract_fixed_field( bib_id, 'Date2'), ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
-
-    RETURN;
-END;
-$func$ LANGUAGE PLPGSQL;
-
 CREATE OR REPLACE FUNCTION metabib.reingest_metabib_full_rec( bib_id BIGINT ) RETURNS VOID AS $func$
 BEGIN
     PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
@@ -917,10 +925,19 @@ $func$ LANGUAGE SQL;
 
 -- AFTER UPDATE OR INSERT trigger for biblio.record_entry
 CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+DECLARE
+    transformed_xml TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_value      TEXT;
+    new_attrs       HSTORE := ''::HSTORE;
+    attr_def        config.record_attr_definition%ROWTYPE;
 BEGIN
 
     IF NEW.deleted IS TRUE THEN -- If this bib is deleted
         DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
+        DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
         DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
         RETURN NEW; -- and we're done
     END IF;
@@ -943,9 +960,92 @@ BEGIN
     PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
     IF NOT FOUND THEN
         PERFORM metabib.reingest_metabib_full_rec(NEW.id);
+
+        -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
         PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
         IF NOT FOUND THEN
-            PERFORM metabib.reingest_metabib_rec_descriptor(NEW.id);
+            FOR attr_def IN SELECT * FROM config.record_attr_definition ORDER BY format LOOP
+
+                IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+                    SELECT  ARRAY_TO_STRING(ARRAY_ACCUM(value), COALESCE(attr_def.joiner,' ')) INTO attr_value
+                      FROM  (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
+                      WHERE record = NEW.id
+                            AND tag LIKE attr_def.tag
+                            AND CASE
+                                WHEN attr_def.sf_list IS NOT NULL 
+                                    THEN POSITION(subfield IN attr_def.sf_list) > 0
+                                ELSE TRUE
+                                END
+                      GROUP BY tag
+                      ORDER BY tag
+                      LIMIT 1;
+
+                ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+                    attr_value := biblio.marc21_extract_fixed_field(NEW.id, attr_def.fixed_field);
+
+                ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+                    SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+            
+                    -- See if we can skip the XSLT ... it's expensive
+                    IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                        -- Can't skip the transform
+                        IF xfrm.xslt <> '---' THEN
+                            transformed_xml := oils_xslt_process(NEW.marc,xfrm.xslt);
+                        ELSE
+                            transformed_xml := NEW.marc;
+                        END IF;
+            
+                        prev_xfrm := xfrm.name;
+                    END IF;
+
+                    IF xfrm.name IS NULL THEN
+                        -- just grab the marcxml (empty) transform
+                        SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                        prev_xfrm := xfrm.name;
+                    END IF;
+
+                    attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
+
+                ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+                    SELECT  value::TEXT INTO attr_value
+                      FROM  biblio.marc21_physical_characteristics(NEW.id)
+                      WHERE subfield = attr_def.phys_char_sf
+                      LIMIT 1; -- Just in case ...
+
+                END IF;
+
+                -- apply index normalizers to attr_value
+                FOR normalizer IN
+                    SELECT  n.func AS func,
+                            n.param_count AS param_count,
+                            m.params AS params
+                      FROM  config.index_normalizer n
+                            JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+                      WHERE attr = attr_def.name
+                      ORDER BY m.pos LOOP
+                        EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                            quote_literal( attr_value ) ||
+                            CASE
+                                WHEN normalizer.param_count > 0
+                                    THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                                    ELSE ''
+                                END ||
+                            ')' INTO attr_value;
+        
+                END LOOP;
+
+                -- Add the new value to the hstore
+                new_attrs := new_attrs || hstore( attr_def.name, attr_value );
+
+            END LOOP;
+
+            IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
+                INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
+            ELSE
+                UPDATE metabib.record_attr SET attrs = attrs || new_attrs WHERE id = NEW.id;
+            END IF;
+
         END IF;
     END IF;
 
index c7c1448..18357ef 100644 (file)
@@ -363,33 +363,6 @@ CREATE TABLE asset.copy_template (
        mint_condition BOOL
 );
 
-CREATE VIEW stats.fleshed_copy AS 
-        SELECT  cp.*,
-               CAST(cp.create_date AS DATE) AS create_date_day,
-               CAST(cp.edit_date AS DATE) AS edit_date_day,
-               DATE_TRUNC('hour', cp.create_date) AS create_date_hour,
-               DATE_TRUNC('hour', cp.edit_date) AS edit_date_hour,
-                cn.label AS call_number_label,
-                cn.owning_lib,
-                rd.item_lang,
-                rd.item_type,
-                rd.item_form
-        FROM    asset.copy cp
-                JOIN asset.call_number cn ON (cp.call_number = cn.id)
-                JOIN metabib.rec_descriptor rd ON (rd.record = cn.record);
-
-CREATE VIEW stats.fleshed_call_number AS 
-        SELECT  cn.*,
-                       CAST(cn.create_date AS DATE) AS create_date_day,
-               CAST(cn.edit_date AS DATE) AS edit_date_day,
-               DATE_TRUNC('hour', cn.create_date) AS create_date_hour,
-               DATE_TRUNC('hour', cn.edit_date) AS edit_date_hour,
-               rd.item_lang,
-                rd.item_type,
-                rd.item_form
-        FROM    asset.call_number cn
-                JOIN metabib.rec_descriptor rd ON (rd.record = cn.record);
-
 CREATE OR REPLACE FUNCTION asset.opac_ou_record_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
 DECLARE
     ans RECORD;
index fc86d8a..cb7391a 100644 (file)
@@ -290,21 +290,6 @@ CREATE OR REPLACE VIEW action.billable_circulations AS
          FROM  action.circulation
          WHERE xact_finish IS NULL;
 
-CREATE VIEW stats.fleshed_circulation AS
-        SELECT  c.*,
-                CAST(c.xact_start AS DATE) AS start_date_day,
-                CAST(c.xact_finish AS DATE) AS finish_date_day,
-                DATE_TRUNC('hour', c.xact_start) AS start_date_hour,
-                DATE_TRUNC('hour', c.xact_finish) AS finish_date_hour,
-                cp.call_number_label,
-                cp.owning_lib,
-                cp.item_lang,
-                cp.item_type,
-                cp.item_form
-        FROM    "action".circulation c
-                JOIN stats.fleshed_copy cp ON (cp.id = c.target_copy);
-
-
 CREATE OR REPLACE FUNCTION action.circulation_claims_returned () RETURNS TRIGGER AS $$
 BEGIN
        IF OLD.stop_fines IS NULL OR OLD.stop_fines <> NEW.stop_fines THEN
index e0f3b3d..c4e3b5b 100644 (file)
@@ -41,32 +41,6 @@ INSERT INTO config.circ_modifier (code, name, description, sip2_media_type )
 -- add an fkey pointing to the new circ mod table
 ALTER TABLE asset.copy ADD CONSTRAINT circ_mod_fkey FOREIGN KEY (circ_modifier) REFERENCES config.circ_modifier (code) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 
--- config table to hold the vr_format names
-CREATE TABLE config.videorecording_format_map (
-    code    TEXT    PRIMARY KEY,
-    value    TEXT    NOT NULL
-);
-
-INSERT INTO config.videorecording_format_map VALUES ('a','Beta');
-INSERT INTO config.videorecording_format_map VALUES ('b','VHS');
-INSERT INTO config.videorecording_format_map VALUES ('c','U-matic');
-INSERT INTO config.videorecording_format_map VALUES ('d','EIAJ');
-INSERT INTO config.videorecording_format_map VALUES ('e','Type C');
-INSERT INTO config.videorecording_format_map VALUES ('f','Quadruplex');
-INSERT INTO config.videorecording_format_map VALUES ('g','Laserdisc');
-INSERT INTO config.videorecording_format_map VALUES ('h','CED');
-INSERT INTO config.videorecording_format_map VALUES ('i','Betacam');
-INSERT INTO config.videorecording_format_map VALUES ('j','Betacam SP');
-INSERT INTO config.videorecording_format_map VALUES ('k','Super-VHS');
-INSERT INTO config.videorecording_format_map VALUES ('m','M-II');
-INSERT INTO config.videorecording_format_map VALUES ('o','D-2');
-INSERT INTO config.videorecording_format_map VALUES ('p','8 mm.');
-INSERT INTO config.videorecording_format_map VALUES ('q','Hi-8 mm.');
-INSERT INTO config.videorecording_format_map VALUES ('u','Unknown');
-INSERT INTO config.videorecording_format_map VALUES ('v','DVD');
-INSERT INTO config.videorecording_format_map VALUES ('z','Other');
-
-
 
 /**
  **  Here we define the tables that make up the circ matrix.  Conceptually, this implements
@@ -83,9 +57,9 @@ CREATE TABLE config.circ_matrix_matchpoint (
     org_unit             INT        NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,    -- Set to the top OU for the matchpoint applicability range; we can use org_unit_prox to choose the "best"
     grp                  INT     NOT NULL REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED,    -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
     circ_modifier        TEXT    REFERENCES config.circ_modifier (code) DEFERRABLE INITIALLY DEFERRED,
-    marc_type            TEXT    REFERENCES config.item_type_map (code) DEFERRABLE INITIALLY DEFERRED,
-    marc_form            TEXT    REFERENCES config.item_form_map (code) DEFERRABLE INITIALLY DEFERRED,
-    marc_vr_format       TEXT    REFERENCES config.videorecording_format_map (code) DEFERRABLE INITIALLY DEFERRED,
+    marc_type            TEXT,
+    marc_form            TEXT,
+    marc_vr_format       TEXT,
     copy_circ_lib        INT     REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
     copy_owning_lib      INT     REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
     user_home_ou         INT     REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
index a025979..3b954af 100644 (file)
@@ -41,9 +41,9 @@ CREATE TABLE config.hold_matrix_matchpoint (
     usr_grp                 INT        REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED,    -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
     requestor_grp           INT        NOT NULL REFERENCES permission.grp_tree (id) DEFERRABLE INITIALLY DEFERRED,    -- Set to the top applicable group from the group tree; will need descendents and prox functions for filtering
     circ_modifier           TEXT    REFERENCES config.circ_modifier (code) DEFERRABLE INITIALLY DEFERRED,
-    marc_type               TEXT    REFERENCES config.item_type_map (code) DEFERRABLE INITIALLY DEFERRED,
-    marc_form               TEXT    REFERENCES config.item_form_map (code) DEFERRABLE INITIALLY DEFERRED,
-    marc_vr_format          TEXT    REFERENCES config.videorecording_format_map (code) DEFERRABLE INITIALLY DEFERRED,
+    marc_type               TEXT,
+    marc_form               TEXT,
+    marc_vr_format          TEXT,
     juvenile_flag           BOOL,
     ref_flag                BOOL,
     -- "Result" Fields
index 346ed98..619441d 100644 (file)
@@ -69,8 +69,6 @@ ALTER TABLE metabib.keyword_field_entry ADD CONSTRAINT metabib_keyword_field_ent
 ALTER TABLE metabib.series_field_entry ADD CONSTRAINT metabib_series_field_entry_source_pkey FOREIGN KEY (source) REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 ALTER TABLE metabib.series_field_entry ADD CONSTRAINT metabib_series_field_entry_field_pkey FOREIGN KEY (field) REFERENCES config.metabib_field (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 
-ALTER TABLE metabib.rec_descriptor ADD CONSTRAINT metabib_rec_descriptor_record_fkey FOREIGN KEY (record) REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
-
 ALTER TABLE metabib.real_full_rec ADD CONSTRAINT metabib_full_rec_record_fkey FOREIGN KEY (record) REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 
 ALTER TABLE metabib.metarecord_source_map ADD CONSTRAINT metabib_metarecord_source_map_source_fkey FOREIGN KEY (source) REFERENCES biblio.record_entry (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
index ad5dbb2..e0727e9 100644 (file)
@@ -26,6 +26,7 @@ INSERT INTO config.xml_transform VALUES ( 'mods32', 'http://www.loc.gov/mods/v3'
 INSERT INTO config.xml_transform VALUES ( 'mods33', 'http://www.loc.gov/mods/v3', 'mods33', '');
 INSERT INTO config.xml_transform VALUES ( 'marc21expand880', 'http://www.loc.gov/MARC21/slim', 'marc', '' );
 
+-- Index Definitions
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, facet_field ) VALUES 
     (1, 'series', 'seriestitle', oils_i18n_gettext(1, 'Series Title', 'cmf', 'label'), 'mods32', $$//mods32:mods/mods32:relatedItem[@type="series"]/mods32:titleInfo$$, TRUE );
 
@@ -62,7 +63,7 @@ INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath,
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES 
     (15, 'keyword', 'keyword', oils_i18n_gettext(15, 'General Keywords', 'cmf', 'label'), 'mods32', $$//mods32:mods/*[not(local-name()='originInfo')]$$ ); -- /* to fool vim */;
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
-    (16, 'subject', 'complete', oils_i18n_gettext(16, 'All Subjects', 'cmf', 'label'), 'mods32', $$//mods32:mods/mods32:subject//text()$$ );
+    (16, 'subject', 'complete', oils_i18n_gettext(16, 'All Subjects', 'cmf', 'label'), 'mods32', $$//mods32:mods/mods32:subject$$ );
 
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
     (17, 'identifier', 'accession', oils_i18n_gettext(17, 'Accession Number', 'cmf', 'label'), 'marcxml', $$//marc:controlfield[@tag='001']$$ );
@@ -71,17 +72,17 @@ INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath )
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
     (19, 'identifier', 'issn', oils_i18n_gettext(19, 'ISSN', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='022']/marc:subfield[@code='a' or @code='z']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
-    (20, 'identifier', 'upc', oils_i18n_gettext(20, 'UPC', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and ind1='1']/marc:subfield[@code='a' or @code='z']$$ );
+    (20, 'identifier', 'upc', oils_i18n_gettext(20, 'UPC', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and @ind1='1']/marc:subfield[@code='a' or @code='z']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
-    (21, 'identifier', 'ismn', oils_i18n_gettext(21, 'ISMN', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and ind1='2']/marc:subfield[@code='a' or @code='z']$$ );
+    (21, 'identifier', 'ismn', oils_i18n_gettext(21, 'ISMN', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and @ind1='2']/marc:subfield[@code='a' or @code='z']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
-    (22, 'identifier', 'ean', oils_i18n_gettext(22, 'EAN', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and ind1='3']/marc:subfield[@code='a' or @code='z']$$ );
+    (22, 'identifier', 'ean', oils_i18n_gettext(22, 'EAN', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and @ind1='3']/marc:subfield[@code='a' or @code='z']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
-    (23, 'identifier', 'isrc', oils_i18n_gettext(23, 'ISRC', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and ind1='0']/marc:subfield[@code='a' or @code='z']$$ );
+    (23, 'identifier', 'isrc', oils_i18n_gettext(23, 'ISRC', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and @ind1='0']/marc:subfield[@code='a' or @code='z']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
-    (24, 'identifier', 'sici', oils_i18n_gettext(24, 'SICI', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and ind1='4']/marc:subfield[@code='a' or @code='z']$$ );
+    (24, 'identifier', 'sici', oils_i18n_gettext(24, 'SICI', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='024' and @ind1='4']/marc:subfield[@code='a' or @code='z']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
-    (25, 'identifier', 'bibcn', oils_i18n_gettext(25, 'Local Free-Text Call Number', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='099']//text()$$ );
+    (25, 'identifier', 'bibcn', oils_i18n_gettext(25, 'Local Free-Text Call Number', 'cmf', 'label'), 'marcxml', $$//marc:datafield[@tag='099']$$ );
 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath ) VALUES
     (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
@@ -233,590 +234,6 @@ INSERT INTO config.net_access_level (id, name) VALUES
     (3, oils_i18n_gettext(3, 'No Access', 'cnal', 'name'));
 SELECT SETVAL('config.net_access_level_id_seq'::TEXT, 100);
 
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('', oils_i18n_gettext('', 'Unknown or unspecified', 'cam', 'value'), 
-       oils_i18n_gettext('', 'The target audience for the item not known or not specified.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('a', oils_i18n_gettext('a', 'Preschool', 'cam', 'value'),
-       oils_i18n_gettext('a', 'The item is intended for children, approximate ages 0-5 years.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('b', oils_i18n_gettext('b', 'Primary', 'cam', 'value'),
-       oils_i18n_gettext('b', 'The item is intended for children, approximate ages 6-8 years.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('c', oils_i18n_gettext('c', 'Pre-adolescent', 'cam', 'value'),
-       oils_i18n_gettext('c', 'The item is intended for young people, approximate ages 9-13 years.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('d', oils_i18n_gettext('d', 'Adolescent', 'cam', 'value'),
-    oils_i18n_gettext('d', 'The item is intended for young people, approximate ages 14-17 years.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('e', oils_i18n_gettext('e', 'Adult', 'cam', 'value'),
-       oils_i18n_gettext('e', 'The item is intended for adults.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('f', oils_i18n_gettext('f', 'Specialized', 'cam', 'value'),
-       oils_i18n_gettext('f', 'The item is aimed at a particular audience and the nature of the presentation makes the item of little interest to another audience.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('g', oils_i18n_gettext('g', 'General', 'cam', 'value'),
-       oils_i18n_gettext('g', 'The item is of general interest and not aimed at an audience of a particular intellectual level.', 'cam', 'description'));
-INSERT INTO config.audience_map (code, value, description) VALUES 
-    ('j', oils_i18n_gettext('j', 'Juvenile', 'cam', 'value'),
-       oils_i18n_gettext('j', 'The item is intended for children and young people, approximate ages 0-15 years.', 'cam', 'description'));
-
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('0', oils_i18n_gettext('0', 'Not fiction (not further specified)', 'clfm', 'value'),
-       oils_i18n_gettext('0', 'The item is not a work of fiction and no further identification of the literary form is desired', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('1', oils_i18n_gettext('1', 'Fiction (not further specified)', 'clfm', 'value'),
-       oils_i18n_gettext('1', 'The item is a work of fiction and no further identification of the literary form is desired', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('c', oils_i18n_gettext('c', 'Comic strips', 'clfm', 'value'), NULL);
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('d', oils_i18n_gettext('d', 'Dramas', 'clfm', 'value'), NULL);
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('e', oils_i18n_gettext('e', 'Essays', 'clfm', 'value'), NULL);
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('f', oils_i18n_gettext('f', 'Novels', 'clfm', 'value'), NULL);
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('h', oils_i18n_gettext('h', 'Humor, satires, etc.', 'clfm', 'value'),
-       oils_i18n_gettext('h', 'The item is a humorous work, satire or of similar literary form.', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('i', oils_i18n_gettext('i', 'Letters', 'clfm', 'value'),
-       oils_i18n_gettext('i', 'The item is a single letter or collection of correspondence.', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('j', oils_i18n_gettext('j', 'Short stories', 'clfm', 'value'),
-       oils_i18n_gettext('j', 'The item is a short story or collection of short stories.', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('m', oils_i18n_gettext('m', 'Mixed forms', 'clfm', 'value'),
-       oils_i18n_gettext('m', 'The item is a variety of literary forms (e.g., poetry and short stories).', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('p', oils_i18n_gettext('p', 'Poetry', 'clfm', 'value'),
-       oils_i18n_gettext('p', 'The item is a poem or collection of poems.', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('s', oils_i18n_gettext('s', 'Speeches', 'clfm', 'value'),
-       oils_i18n_gettext('s', 'The item is a speech or collection of speeches.', 'clfm', 'description'));
-INSERT INTO config.lit_form_map (code, value, description) VALUES 
-    ('u', oils_i18n_gettext('u', 'Unknown', 'clfm', 'value'),
-       oils_i18n_gettext('u', 'The literary form of the item is unknown.', 'clfm', 'description'));
-
--- TO-DO: Auto-generate these values from CLDR
--- XXX These are the values used in MARC records ... does that match CLDR, including deprecated languages?
-INSERT INTO config.language_map (code, value) VALUES ('aar', oils_i18n_gettext('aar', 'Afar', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('abk', oils_i18n_gettext('abk', 'Abkhaz', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ace', oils_i18n_gettext('ace', 'Achinese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ach', oils_i18n_gettext('ach', 'Acoli', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ada', oils_i18n_gettext('ada', 'Adangme', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ady', oils_i18n_gettext('ady', 'Adygei', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('afa', oils_i18n_gettext('afa', 'Afroasiatic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('afh', oils_i18n_gettext('afh', 'Afrihili (Artificial language)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('afr', oils_i18n_gettext('afr', 'Afrikaans', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-ajm', oils_i18n_gettext('-ajm', 'Aljamía', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('aka', oils_i18n_gettext('aka', 'Akan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('akk', oils_i18n_gettext('akk', 'Akkadian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('alb', oils_i18n_gettext('alb', 'Albanian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ale', oils_i18n_gettext('ale', 'Aleut', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('alg', oils_i18n_gettext('alg', 'Algonquian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('amh', oils_i18n_gettext('amh', 'Amharic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ang', oils_i18n_gettext('ang', 'English, Old (ca. 450-1100)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('apa', oils_i18n_gettext('apa', 'Apache languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ara', oils_i18n_gettext('ara', 'Arabic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('arc', oils_i18n_gettext('arc', 'Aramaic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('arg', oils_i18n_gettext('arg', 'Aragonese Spanish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('arm', oils_i18n_gettext('arm', 'Armenian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('arn', oils_i18n_gettext('arn', 'Mapuche', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('arp', oils_i18n_gettext('arp', 'Arapaho', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('art', oils_i18n_gettext('art', 'Artificial (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('arw', oils_i18n_gettext('arw', 'Arawak', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('asm', oils_i18n_gettext('asm', 'Assamese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ast', oils_i18n_gettext('ast', 'Bable', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ath', oils_i18n_gettext('ath', 'Athapascan (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('aus', oils_i18n_gettext('aus', 'Australian languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ava', oils_i18n_gettext('ava', 'Avaric', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ave', oils_i18n_gettext('ave', 'Avestan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('awa', oils_i18n_gettext('awa', 'Awadhi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('aym', oils_i18n_gettext('aym', 'Aymara', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('aze', oils_i18n_gettext('aze', 'Azerbaijani', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bad', oils_i18n_gettext('bad', 'Banda', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bai', oils_i18n_gettext('bai', 'Bamileke languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bak', oils_i18n_gettext('bak', 'Bashkir', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bal', oils_i18n_gettext('bal', 'Baluchi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bam', oils_i18n_gettext('bam', 'Bambara', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ban', oils_i18n_gettext('ban', 'Balinese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('baq', oils_i18n_gettext('baq', 'Basque', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bas', oils_i18n_gettext('bas', 'Basa', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bat', oils_i18n_gettext('bat', 'Baltic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bej', oils_i18n_gettext('bej', 'Beja', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bel', oils_i18n_gettext('bel', 'Belarusian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bem', oils_i18n_gettext('bem', 'Bemba', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ben', oils_i18n_gettext('ben', 'Bengali', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ber', oils_i18n_gettext('ber', 'Berber (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bho', oils_i18n_gettext('bho', 'Bhojpuri', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bih', oils_i18n_gettext('bih', 'Bihari', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bik', oils_i18n_gettext('bik', 'Bikol', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bin', oils_i18n_gettext('bin', 'Edo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bis', oils_i18n_gettext('bis', 'Bislama', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bla', oils_i18n_gettext('bla', 'Siksika', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bnt', oils_i18n_gettext('bnt', 'Bantu (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bos', oils_i18n_gettext('bos', 'Bosnian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bra', oils_i18n_gettext('bra', 'Braj', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bre', oils_i18n_gettext('bre', 'Breton', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('btk', oils_i18n_gettext('btk', 'Batak', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bua', oils_i18n_gettext('bua', 'Buriat', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bug', oils_i18n_gettext('bug', 'Bugis', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bul', oils_i18n_gettext('bul', 'Bulgarian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('bur', oils_i18n_gettext('bur', 'Burmese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cad', oils_i18n_gettext('cad', 'Caddo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cai', oils_i18n_gettext('cai', 'Central American Indian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-cam', oils_i18n_gettext('-cam', 'Khmer', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('car', oils_i18n_gettext('car', 'Carib', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cat', oils_i18n_gettext('cat', 'Catalan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cau', oils_i18n_gettext('cau', 'Caucasian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ceb', oils_i18n_gettext('ceb', 'Cebuano', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cel', oils_i18n_gettext('cel', 'Celtic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cha', oils_i18n_gettext('cha', 'Chamorro', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chb', oils_i18n_gettext('chb', 'Chibcha', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('che', oils_i18n_gettext('che', 'Chechen', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chg', oils_i18n_gettext('chg', 'Chagatai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chi', oils_i18n_gettext('chi', 'Chinese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chk', oils_i18n_gettext('chk', 'Truk', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chm', oils_i18n_gettext('chm', 'Mari', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chn', oils_i18n_gettext('chn', 'Chinook jargon', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cho', oils_i18n_gettext('cho', 'Choctaw', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chp', oils_i18n_gettext('chp', 'Chipewyan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chr', oils_i18n_gettext('chr', 'Cherokee', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chu', oils_i18n_gettext('chu', 'Church Slavic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chv', oils_i18n_gettext('chv', 'Chuvash', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('chy', oils_i18n_gettext('chy', 'Cheyenne', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cmc', oils_i18n_gettext('cmc', 'Chamic languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cop', oils_i18n_gettext('cop', 'Coptic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cor', oils_i18n_gettext('cor', 'Cornish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cos', oils_i18n_gettext('cos', 'Corsican', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cpe', oils_i18n_gettext('cpe', 'Creoles and Pidgins, English-based (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cpf', oils_i18n_gettext('cpf', 'Creoles and Pidgins, French-based (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cpp', oils_i18n_gettext('cpp', 'Creoles and Pidgins, Portuguese-based (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cre', oils_i18n_gettext('cre', 'Cree', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('crh', oils_i18n_gettext('crh', 'Crimean Tatar', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('crp', oils_i18n_gettext('crp', 'Creoles and Pidgins (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cus', oils_i18n_gettext('cus', 'Cushitic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('cze', oils_i18n_gettext('cze', 'Czech', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dak', oils_i18n_gettext('dak', 'Dakota', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dan', oils_i18n_gettext('dan', 'Danish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dar', oils_i18n_gettext('dar', 'Dargwa', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('day', oils_i18n_gettext('day', 'Dayak', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('del', oils_i18n_gettext('del', 'Delaware', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('den', oils_i18n_gettext('den', 'Slave', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dgr', oils_i18n_gettext('dgr', 'Dogrib', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('din', oils_i18n_gettext('din', 'Dinka', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('div', oils_i18n_gettext('div', 'Divehi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('doi', oils_i18n_gettext('doi', 'Dogri', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dra', oils_i18n_gettext('dra', 'Dravidian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dua', oils_i18n_gettext('dua', 'Duala', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dum', oils_i18n_gettext('dum', 'Dutch, Middle (ca. 1050-1350)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dut', oils_i18n_gettext('dut', 'Dutch', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dyu', oils_i18n_gettext('dyu', 'Dyula', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('dzo', oils_i18n_gettext('dzo', 'Dzongkha', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('efi', oils_i18n_gettext('efi', 'Efik', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('egy', oils_i18n_gettext('egy', 'Egyptian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('eka', oils_i18n_gettext('eka', 'Ekajuk', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('elx', oils_i18n_gettext('elx', 'Elamite', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('eng', oils_i18n_gettext('eng', 'English', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('enm', oils_i18n_gettext('enm', 'English, Middle (1100-1500)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('epo', oils_i18n_gettext('epo', 'Esperanto', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-esk', oils_i18n_gettext('-esk', 'Eskimo languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-esp', oils_i18n_gettext('-esp', 'Esperanto', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('est', oils_i18n_gettext('est', 'Estonian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-eth', oils_i18n_gettext('-eth', 'Ethiopic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ewe', oils_i18n_gettext('ewe', 'Ewe', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ewo', oils_i18n_gettext('ewo', 'Ewondo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fan', oils_i18n_gettext('fan', 'Fang', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fao', oils_i18n_gettext('fao', 'Faroese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-far', oils_i18n_gettext('-far', 'Faroese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fat', oils_i18n_gettext('fat', 'Fanti', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fij', oils_i18n_gettext('fij', 'Fijian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fin', oils_i18n_gettext('fin', 'Finnish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fiu', oils_i18n_gettext('fiu', 'Finno-Ugrian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fon', oils_i18n_gettext('fon', 'Fon', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fre', oils_i18n_gettext('fre', 'French', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-fri', oils_i18n_gettext('-fri', 'Frisian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('frm', oils_i18n_gettext('frm', 'French, Middle (ca. 1400-1600)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fro', oils_i18n_gettext('fro', 'French, Old (ca. 842-1400)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fry', oils_i18n_gettext('fry', 'Frisian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ful', oils_i18n_gettext('ful', 'Fula', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('fur', oils_i18n_gettext('fur', 'Friulian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gaa', oils_i18n_gettext('gaa', 'Gã', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-gae', oils_i18n_gettext('-gae', 'Scottish Gaelic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-gag', oils_i18n_gettext('-gag', 'Galician', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-gal', oils_i18n_gettext('-gal', 'Oromo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gay', oils_i18n_gettext('gay', 'Gayo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gba', oils_i18n_gettext('gba', 'Gbaya', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gem', oils_i18n_gettext('gem', 'Germanic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('geo', oils_i18n_gettext('geo', 'Georgian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ger', oils_i18n_gettext('ger', 'German', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gez', oils_i18n_gettext('gez', 'Ethiopic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gil', oils_i18n_gettext('gil', 'Gilbertese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gla', oils_i18n_gettext('gla', 'Scottish Gaelic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gle', oils_i18n_gettext('gle', 'Irish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('glg', oils_i18n_gettext('glg', 'Galician', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('glv', oils_i18n_gettext('glv', 'Manx', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gmh', oils_i18n_gettext('gmh', 'German, Middle High (ca. 1050-1500)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('goh', oils_i18n_gettext('goh', 'German, Old High (ca. 750-1050)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gon', oils_i18n_gettext('gon', 'Gondi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gor', oils_i18n_gettext('gor', 'Gorontalo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('got', oils_i18n_gettext('got', 'Gothic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('grb', oils_i18n_gettext('grb', 'Grebo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('grc', oils_i18n_gettext('grc', 'Greek, Ancient (to 1453)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gre', oils_i18n_gettext('gre', 'Greek, Modern (1453- )', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('grn', oils_i18n_gettext('grn', 'Guarani', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-gua', oils_i18n_gettext('-gua', 'Guarani', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('guj', oils_i18n_gettext('guj', 'Gujarati', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('gwi', oils_i18n_gettext('gwi', 'Gwich', 'clm', 'value''in'));
-INSERT INTO config.language_map (code, value) VALUES ('hai', oils_i18n_gettext('hai', 'Haida', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hat', oils_i18n_gettext('hat', 'Haitian French Creole', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hau', oils_i18n_gettext('hau', 'Hausa', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('haw', oils_i18n_gettext('haw', 'Hawaiian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('heb', oils_i18n_gettext('heb', 'Hebrew', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('her', oils_i18n_gettext('her', 'Herero', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hil', oils_i18n_gettext('hil', 'Hiligaynon', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('him', oils_i18n_gettext('him', 'Himachali', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hin', oils_i18n_gettext('hin', 'Hindi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hit', oils_i18n_gettext('hit', 'Hittite', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hmn', oils_i18n_gettext('hmn', 'Hmong', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hmo', oils_i18n_gettext('hmo', 'Hiri Motu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hun', oils_i18n_gettext('hun', 'Hungarian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('hup', oils_i18n_gettext('hup', 'Hupa', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('iba', oils_i18n_gettext('iba', 'Iban', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ibo', oils_i18n_gettext('ibo', 'Igbo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ice', oils_i18n_gettext('ice', 'Icelandic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ido', oils_i18n_gettext('ido', 'Ido', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('iii', oils_i18n_gettext('iii', 'Sichuan Yi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ijo', oils_i18n_gettext('ijo', 'Ijo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('iku', oils_i18n_gettext('iku', 'Inuktitut', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ile', oils_i18n_gettext('ile', 'Interlingue', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ilo', oils_i18n_gettext('ilo', 'Iloko', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ina', oils_i18n_gettext('ina', 'Interlingua (International Auxiliary Language Association)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('inc', oils_i18n_gettext('inc', 'Indic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ind', oils_i18n_gettext('ind', 'Indonesian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ine', oils_i18n_gettext('ine', 'Indo-European (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('inh', oils_i18n_gettext('inh', 'Ingush', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-int', oils_i18n_gettext('-int', 'Interlingua (International Auxiliary Language Association)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ipk', oils_i18n_gettext('ipk', 'Inupiaq', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ira', oils_i18n_gettext('ira', 'Iranian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-iri', oils_i18n_gettext('-iri', 'Irish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('iro', oils_i18n_gettext('iro', 'Iroquoian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ita', oils_i18n_gettext('ita', 'Italian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('jav', oils_i18n_gettext('jav', 'Javanese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('jpn', oils_i18n_gettext('jpn', 'Japanese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('jpr', oils_i18n_gettext('jpr', 'Judeo-Persian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('jrb', oils_i18n_gettext('jrb', 'Judeo-Arabic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kaa', oils_i18n_gettext('kaa', 'Kara-Kalpak', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kab', oils_i18n_gettext('kab', 'Kabyle', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kac', oils_i18n_gettext('kac', 'Kachin', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kal', oils_i18n_gettext('kal', 'Kalâtdlisut', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kam', oils_i18n_gettext('kam', 'Kamba', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kan', oils_i18n_gettext('kan', 'Kannada', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kar', oils_i18n_gettext('kar', 'Karen', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kas', oils_i18n_gettext('kas', 'Kashmiri', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kau', oils_i18n_gettext('kau', 'Kanuri', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kaw', oils_i18n_gettext('kaw', 'Kawi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kaz', oils_i18n_gettext('kaz', 'Kazakh', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kbd', oils_i18n_gettext('kbd', 'Kabardian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kha', oils_i18n_gettext('kha', 'Khasi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('khi', oils_i18n_gettext('khi', 'Khoisan (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('khm', oils_i18n_gettext('khm', 'Khmer', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kho', oils_i18n_gettext('kho', 'Khotanese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kik', oils_i18n_gettext('kik', 'Kikuyu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kin', oils_i18n_gettext('kin', 'Kinyarwanda', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kir', oils_i18n_gettext('kir', 'Kyrgyz', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kmb', oils_i18n_gettext('kmb', 'Kimbundu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kok', oils_i18n_gettext('kok', 'Konkani', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kom', oils_i18n_gettext('kom', 'Komi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kon', oils_i18n_gettext('kon', 'Kongo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kor', oils_i18n_gettext('kor', 'Korean', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kos', oils_i18n_gettext('kos', 'Kusaie', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kpe', oils_i18n_gettext('kpe', 'Kpelle', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kro', oils_i18n_gettext('kro', 'Kru', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kru', oils_i18n_gettext('kru', 'Kurukh', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kua', oils_i18n_gettext('kua', 'Kuanyama', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kum', oils_i18n_gettext('kum', 'Kumyk', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kur', oils_i18n_gettext('kur', 'Kurdish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-kus', oils_i18n_gettext('-kus', 'Kusaie', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('kut', oils_i18n_gettext('kut', 'Kutenai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lad', oils_i18n_gettext('lad', 'Ladino', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lah', oils_i18n_gettext('lah', 'Lahnda', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lam', oils_i18n_gettext('lam', 'Lamba', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-lan', oils_i18n_gettext('-lan', 'Occitan (post-1500)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lao', oils_i18n_gettext('lao', 'Lao', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-lap', oils_i18n_gettext('-lap', 'Sami', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lat', oils_i18n_gettext('lat', 'Latin', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lav', oils_i18n_gettext('lav', 'Latvian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lez', oils_i18n_gettext('lez', 'Lezgian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lim', oils_i18n_gettext('lim', 'Limburgish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lin', oils_i18n_gettext('lin', 'Lingala', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lit', oils_i18n_gettext('lit', 'Lithuanian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lol', oils_i18n_gettext('lol', 'Mongo-Nkundu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('loz', oils_i18n_gettext('loz', 'Lozi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ltz', oils_i18n_gettext('ltz', 'Letzeburgesch', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lua', oils_i18n_gettext('lua', 'Luba-Lulua', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lub', oils_i18n_gettext('lub', 'Luba-Katanga', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lug', oils_i18n_gettext('lug', 'Ganda', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lui', oils_i18n_gettext('lui', 'Luiseño', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lun', oils_i18n_gettext('lun', 'Lunda', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('luo', oils_i18n_gettext('luo', 'Luo (Kenya and Tanzania)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('lus', oils_i18n_gettext('lus', 'Lushai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mac', oils_i18n_gettext('mac', 'Macedonian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mad', oils_i18n_gettext('mad', 'Madurese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mag', oils_i18n_gettext('mag', 'Magahi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mah', oils_i18n_gettext('mah', 'Marshallese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mai', oils_i18n_gettext('mai', 'Maithili', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mak', oils_i18n_gettext('mak', 'Makasar', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mal', oils_i18n_gettext('mal', 'Malayalam', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('man', oils_i18n_gettext('man', 'Mandingo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mao', oils_i18n_gettext('mao', 'Maori', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('map', oils_i18n_gettext('map', 'Austronesian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mar', oils_i18n_gettext('mar', 'Marathi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mas', oils_i18n_gettext('mas', 'Masai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-max', oils_i18n_gettext('-max', 'Manx', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('may', oils_i18n_gettext('may', 'Malay', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mdr', oils_i18n_gettext('mdr', 'Mandar', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('men', oils_i18n_gettext('men', 'Mende', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mga', oils_i18n_gettext('mga', 'Irish, Middle (ca. 1100-1550)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mic', oils_i18n_gettext('mic', 'Micmac', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('min', oils_i18n_gettext('min', 'Minangkabau', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mis', oils_i18n_gettext('mis', 'Miscellaneous languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mkh', oils_i18n_gettext('mkh', 'Mon-Khmer (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-mla', oils_i18n_gettext('-mla', 'Malagasy', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mlg', oils_i18n_gettext('mlg', 'Malagasy', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mlt', oils_i18n_gettext('mlt', 'Maltese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mnc', oils_i18n_gettext('mnc', 'Manchu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mni', oils_i18n_gettext('mni', 'Manipuri', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mno', oils_i18n_gettext('mno', 'Manobo languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('moh', oils_i18n_gettext('moh', 'Mohawk', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mol', oils_i18n_gettext('mol', 'Moldavian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mon', oils_i18n_gettext('mon', 'Mongolian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mos', oils_i18n_gettext('mos', 'Mooré', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mul', oils_i18n_gettext('mul', 'Multiple languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mun', oils_i18n_gettext('mun', 'Munda (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mus', oils_i18n_gettext('mus', 'Creek', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('mwr', oils_i18n_gettext('mwr', 'Marwari', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('myn', oils_i18n_gettext('myn', 'Mayan languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nah', oils_i18n_gettext('nah', 'Nahuatl', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nai', oils_i18n_gettext('nai', 'North American Indian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nap', oils_i18n_gettext('nap', 'Neapolitan Italian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nau', oils_i18n_gettext('nau', 'Nauru', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nav', oils_i18n_gettext('nav', 'Navajo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nbl', oils_i18n_gettext('nbl', 'Ndebele (South Africa)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nde', oils_i18n_gettext('nde', 'Ndebele (Zimbabwe)  ', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ndo', oils_i18n_gettext('ndo', 'Ndonga', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nds', oils_i18n_gettext('nds', 'Low German', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nep', oils_i18n_gettext('nep', 'Nepali', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('new', oils_i18n_gettext('new', 'Newari', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nia', oils_i18n_gettext('nia', 'Nias', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nic', oils_i18n_gettext('nic', 'Niger-Kordofanian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('niu', oils_i18n_gettext('niu', 'Niuean', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nno', oils_i18n_gettext('nno', 'Norwegian (Nynorsk)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nob', oils_i18n_gettext('nob', 'Norwegian (BokmÃ¥l)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nog', oils_i18n_gettext('nog', 'Nogai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('non', oils_i18n_gettext('non', 'Old Norse', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nor', oils_i18n_gettext('nor', 'Norwegian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nso', oils_i18n_gettext('nso', 'Northern Sotho', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nub', oils_i18n_gettext('nub', 'Nubian languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nya', oils_i18n_gettext('nya', 'Nyanja', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nym', oils_i18n_gettext('nym', 'Nyamwezi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nyn', oils_i18n_gettext('nyn', 'Nyankole', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nyo', oils_i18n_gettext('nyo', 'Nyoro', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('nzi', oils_i18n_gettext('nzi', 'Nzima', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('oci', oils_i18n_gettext('oci', 'Occitan (post-1500)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('oji', oils_i18n_gettext('oji', 'Ojibwa', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ori', oils_i18n_gettext('ori', 'Oriya', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('orm', oils_i18n_gettext('orm', 'Oromo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('osa', oils_i18n_gettext('osa', 'Osage', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('oss', oils_i18n_gettext('oss', 'Ossetic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ota', oils_i18n_gettext('ota', 'Turkish, Ottoman', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('oto', oils_i18n_gettext('oto', 'Otomian languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('paa', oils_i18n_gettext('paa', 'Papuan (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pag', oils_i18n_gettext('pag', 'Pangasinan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pal', oils_i18n_gettext('pal', 'Pahlavi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pam', oils_i18n_gettext('pam', 'Pampanga', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pan', oils_i18n_gettext('pan', 'Panjabi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pap', oils_i18n_gettext('pap', 'Papiamento', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pau', oils_i18n_gettext('pau', 'Palauan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('peo', oils_i18n_gettext('peo', 'Old Persian (ca. 600-400 B.C.)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('per', oils_i18n_gettext('per', 'Persian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('phi', oils_i18n_gettext('phi', 'Philippine (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('phn', oils_i18n_gettext('phn', 'Phoenician', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pli', oils_i18n_gettext('pli', 'Pali', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pol', oils_i18n_gettext('pol', 'Polish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pon', oils_i18n_gettext('pon', 'Ponape', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('por', oils_i18n_gettext('por', 'Portuguese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pra', oils_i18n_gettext('pra', 'Prakrit languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pro', oils_i18n_gettext('pro', 'Provençal (to 1500)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('pus', oils_i18n_gettext('pus', 'Pushto', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('que', oils_i18n_gettext('que', 'Quechua', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('raj', oils_i18n_gettext('raj', 'Rajasthani', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('rap', oils_i18n_gettext('rap', 'Rapanui', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('rar', oils_i18n_gettext('rar', 'Rarotongan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('roa', oils_i18n_gettext('roa', 'Romance (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('roh', oils_i18n_gettext('roh', 'Raeto-Romance', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('rom', oils_i18n_gettext('rom', 'Romani', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('rum', oils_i18n_gettext('rum', 'Romanian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('run', oils_i18n_gettext('run', 'Rundi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('rus', oils_i18n_gettext('rus', 'Russian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sad', oils_i18n_gettext('sad', 'Sandawe', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sag', oils_i18n_gettext('sag', 'Sango (Ubangi Creole)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sah', oils_i18n_gettext('sah', 'Yakut', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sai', oils_i18n_gettext('sai', 'South American Indian (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sal', oils_i18n_gettext('sal', 'Salishan languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sam', oils_i18n_gettext('sam', 'Samaritan Aramaic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('san', oils_i18n_gettext('san', 'Sanskrit', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-sao', oils_i18n_gettext('-sao', 'Samoan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sas', oils_i18n_gettext('sas', 'Sasak', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sat', oils_i18n_gettext('sat', 'Santali', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('scc', oils_i18n_gettext('scc', 'Serbian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sco', oils_i18n_gettext('sco', 'Scots', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('scr', oils_i18n_gettext('scr', 'Croatian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sel', oils_i18n_gettext('sel', 'Selkup', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sem', oils_i18n_gettext('sem', 'Semitic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sga', oils_i18n_gettext('sga', 'Irish, Old (to 1100)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sgn', oils_i18n_gettext('sgn', 'Sign languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('shn', oils_i18n_gettext('shn', 'Shan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-sho', oils_i18n_gettext('-sho', 'Shona', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sid', oils_i18n_gettext('sid', 'Sidamo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sin', oils_i18n_gettext('sin', 'Sinhalese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sio', oils_i18n_gettext('sio', 'Siouan (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sit', oils_i18n_gettext('sit', 'Sino-Tibetan (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sla', oils_i18n_gettext('sla', 'Slavic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('slo', oils_i18n_gettext('slo', 'Slovak', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('slv', oils_i18n_gettext('slv', 'Slovenian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sma', oils_i18n_gettext('sma', 'Southern Sami', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sme', oils_i18n_gettext('sme', 'Northern Sami', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('smi', oils_i18n_gettext('smi', 'Sami', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('smj', oils_i18n_gettext('smj', 'Lule Sami', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('smn', oils_i18n_gettext('smn', 'Inari Sami', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('smo', oils_i18n_gettext('smo', 'Samoan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sms', oils_i18n_gettext('sms', 'Skolt Sami', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sna', oils_i18n_gettext('sna', 'Shona', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('snd', oils_i18n_gettext('snd', 'Sindhi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-snh', oils_i18n_gettext('-snh', 'Sinhalese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('snk', oils_i18n_gettext('snk', 'Soninke', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sog', oils_i18n_gettext('sog', 'Sogdian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('som', oils_i18n_gettext('som', 'Somali', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('son', oils_i18n_gettext('son', 'Songhai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sot', oils_i18n_gettext('sot', 'Sotho', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('spa', oils_i18n_gettext('spa', 'Spanish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('srd', oils_i18n_gettext('srd', 'Sardinian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('srr', oils_i18n_gettext('srr', 'Serer', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ssa', oils_i18n_gettext('ssa', 'Nilo-Saharan (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-sso', oils_i18n_gettext('-sso', 'Sotho', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ssw', oils_i18n_gettext('ssw', 'Swazi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('suk', oils_i18n_gettext('suk', 'Sukuma', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sun', oils_i18n_gettext('sun', 'Sundanese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sus', oils_i18n_gettext('sus', 'Susu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('sux', oils_i18n_gettext('sux', 'Sumerian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('swa', oils_i18n_gettext('swa', 'Swahili', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('swe', oils_i18n_gettext('swe', 'Swedish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-swz', oils_i18n_gettext('-swz', 'Swazi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('syr', oils_i18n_gettext('syr', 'Syriac', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-tag', oils_i18n_gettext('-tag', 'Tagalog', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tah', oils_i18n_gettext('tah', 'Tahitian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tai', oils_i18n_gettext('tai', 'Tai (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-taj', oils_i18n_gettext('-taj', 'Tajik', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tam', oils_i18n_gettext('tam', 'Tamil', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-tar', oils_i18n_gettext('-tar', 'Tatar', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tat', oils_i18n_gettext('tat', 'Tatar', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tel', oils_i18n_gettext('tel', 'Telugu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tem', oils_i18n_gettext('tem', 'Temne', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ter', oils_i18n_gettext('ter', 'Terena', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tet', oils_i18n_gettext('tet', 'Tetum', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tgk', oils_i18n_gettext('tgk', 'Tajik', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tgl', oils_i18n_gettext('tgl', 'Tagalog', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tha', oils_i18n_gettext('tha', 'Thai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tib', oils_i18n_gettext('tib', 'Tibetan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tig', oils_i18n_gettext('tig', 'Tigré', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tir', oils_i18n_gettext('tir', 'Tigrinya', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tiv', oils_i18n_gettext('tiv', 'Tiv', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tkl', oils_i18n_gettext('tkl', 'Tokelauan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tli', oils_i18n_gettext('tli', 'Tlingit', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tmh', oils_i18n_gettext('tmh', 'Tamashek', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tog', oils_i18n_gettext('tog', 'Tonga (Nyasa)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ton', oils_i18n_gettext('ton', 'Tongan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tpi', oils_i18n_gettext('tpi', 'Tok Pisin', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-tru', oils_i18n_gettext('-tru', 'Truk', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tsi', oils_i18n_gettext('tsi', 'Tsimshian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tsn', oils_i18n_gettext('tsn', 'Tswana', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tso', oils_i18n_gettext('tso', 'Tsonga', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('-tsw', oils_i18n_gettext('-tsw', 'Tswana', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tuk', oils_i18n_gettext('tuk', 'Turkmen', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tum', oils_i18n_gettext('tum', 'Tumbuka', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tup', oils_i18n_gettext('tup', 'Tupi languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tur', oils_i18n_gettext('tur', 'Turkish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tut', oils_i18n_gettext('tut', 'Altaic (Other)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tvl', oils_i18n_gettext('tvl', 'Tuvaluan', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('twi', oils_i18n_gettext('twi', 'Twi', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('tyv', oils_i18n_gettext('tyv', 'Tuvinian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('udm', oils_i18n_gettext('udm', 'Udmurt', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('uga', oils_i18n_gettext('uga', 'Ugaritic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('uig', oils_i18n_gettext('uig', 'Uighur', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ukr', oils_i18n_gettext('ukr', 'Ukrainian', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('umb', oils_i18n_gettext('umb', 'Umbundu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('und', oils_i18n_gettext('und', 'Undetermined', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('urd', oils_i18n_gettext('urd', 'Urdu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('uzb', oils_i18n_gettext('uzb', 'Uzbek', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('vai', oils_i18n_gettext('vai', 'Vai', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ven', oils_i18n_gettext('ven', 'Venda', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('vie', oils_i18n_gettext('vie', 'Vietnamese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('vol', oils_i18n_gettext('vol', 'Volapük', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('vot', oils_i18n_gettext('vot', 'Votic', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('wak', oils_i18n_gettext('wak', 'Wakashan languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('wal', oils_i18n_gettext('wal', 'Walamo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('war', oils_i18n_gettext('war', 'Waray', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('was', oils_i18n_gettext('was', 'Washo', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('wel', oils_i18n_gettext('wel', 'Welsh', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('wen', oils_i18n_gettext('wen', 'Sorbian languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('wln', oils_i18n_gettext('wln', 'Walloon', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('wol', oils_i18n_gettext('wol', 'Wolof', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('xal', oils_i18n_gettext('xal', 'Kalmyk', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('xho', oils_i18n_gettext('xho', 'Xhosa', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('yao', oils_i18n_gettext('yao', 'Yao (Africa)', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('yap', oils_i18n_gettext('yap', 'Yapese', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('yid', oils_i18n_gettext('yid', 'Yiddish', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('yor', oils_i18n_gettext('yor', 'Yoruba', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('ypk', oils_i18n_gettext('ypk', 'Yupik languages', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('zap', oils_i18n_gettext('zap', 'Zapotec', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('zen', oils_i18n_gettext('zen', 'Zenaga', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('zha', oils_i18n_gettext('zha', 'Zhuang', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('znd', oils_i18n_gettext('znd', 'Zande', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('zul', oils_i18n_gettext('zul', 'Zulu', 'clm', 'value'));
-INSERT INTO config.language_map (code, value) VALUES ('zun', oils_i18n_gettext('zun', 'Zuni', 'clm', 'value'));
-
-INSERT INTO config.item_form_map (code, value) VALUES ('a', oils_i18n_gettext('a', 'Microfilm', 'cifm', 'value'));
-INSERT INTO config.item_form_map (code, value) VALUES ('b', oils_i18n_gettext('b', 'Microfiche', 'cifm', 'value'));
-INSERT INTO config.item_form_map (code, value) VALUES ('c', oils_i18n_gettext('c', 'Microopaque', 'cifm', 'value'));
-INSERT INTO config.item_form_map (code, value) VALUES ('d', oils_i18n_gettext('d', 'Large print', 'cifm', 'value'));
-INSERT INTO config.item_form_map (code, value) VALUES ('f', oils_i18n_gettext('f', 'Braille', 'cifm', 'value'));
-INSERT INTO config.item_form_map (code, value) VALUES ('r', oils_i18n_gettext('r', 'Regular print reproduction', 'cifm', 'value'));
-INSERT INTO config.item_form_map (code, value) VALUES ('s', oils_i18n_gettext('s', 'Electronic', 'cifm', 'value'));
-
-INSERT INTO config.item_type_map (code, value) VALUES ('a', oils_i18n_gettext('a', 'Language material', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('t', oils_i18n_gettext('t', 'Manuscript language material', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('g', oils_i18n_gettext('g', 'Projected medium', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('k', oils_i18n_gettext('k', 'Two-dimensional nonprojectable graphic', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('r', oils_i18n_gettext('r', 'Three-dimensional artifact or naturally occurring object', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('o', oils_i18n_gettext('o', 'Kit', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('p', oils_i18n_gettext('p', 'Mixed materials', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('e', oils_i18n_gettext('e', 'Cartographic material', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('f', oils_i18n_gettext('f', 'Manuscript cartographic material', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('c', oils_i18n_gettext('c', 'Notated music', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('d', oils_i18n_gettext('d', 'Manuscript notated music', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('i', oils_i18n_gettext('i', 'Nonmusical sound recording', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('j', oils_i18n_gettext('j', 'Musical sound recording', 'citm', 'value'));
-INSERT INTO config.item_type_map (code, value) VALUES ('m', oils_i18n_gettext('m', 'Computer file', 'citm', 'value'));
-
-INSERT INTO config.bib_level_map (code, value) VALUES ('a', oils_i18n_gettext('a', 'Monographic component part', 'cblvl', 'value'));
-INSERT INTO config.bib_level_map (code, value) VALUES ('b', oils_i18n_gettext('b', 'Serial component part', 'cblvl', 'value'));
-INSERT INTO config.bib_level_map (code, value) VALUES ('c', oils_i18n_gettext('c', 'Collection', 'cblvl', 'value'));
-INSERT INTO config.bib_level_map (code, value) VALUES ('d', oils_i18n_gettext('d', 'Subunit', 'cblvl', 'value'));
-INSERT INTO config.bib_level_map (code, value) VALUES ('i', oils_i18n_gettext('i', 'Integrating resource', 'cblvl', 'value'));
-INSERT INTO config.bib_level_map (code, value) VALUES ('m', oils_i18n_gettext('m', 'Monograph/Item', 'cblvl', 'value'));
-INSERT INTO config.bib_level_map (code, value) VALUES ('s', oils_i18n_gettext('s', 'Serial', 'cblvl', 'value'));
-
 -- available locales
 INSERT INTO config.i18n_locale (code,marc_code,name,description)
     VALUES ('en-US', 'eng', oils_i18n_gettext('en-US', 'English (US)', 'i18n_l', 'name'),
@@ -2773,6 +2190,227 @@ INSERT INTO config.org_unit_setting_type
         'interval'
     );
 
+-- 0XXX.patron_registration.sql
+INSERT into config.org_unit_setting_type
+( name, label, description, datatype ) VALUES
+
+( 'ui.patron.edit.au.active.show',
+    oils_i18n_gettext('ui.patron.edit.au.active.show', 'GUI: Show active field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.active.show', 'The active field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.active.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.active.suggest', 'GUI: Suggest active field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.active.suggest', 'The active field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alert_message.show',
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.show', 'GUI: Show alert_message field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.show', 'The alert_message field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alert_message.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.suggest', 'GUI: Suggest alert_message field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.suggest', 'The alert_message field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alias.show',
+    oils_i18n_gettext('ui.patron.edit.au.alias.show', 'GUI: Show alias field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alias.show', 'The alias field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alias.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.alias.suggest', 'GUI: Suggest alias field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alias.suggest', 'The alias field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.barred.show',
+    oils_i18n_gettext('ui.patron.edit.au.barred.show', 'GUI: Show barred field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.barred.show', 'The barred field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.barred.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.barred.suggest', 'GUI: Suggest barred field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.barred.suggest', 'The barred field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_never_checked_out_count.show',
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.show', 'GUI: Show claims_never_checked_out_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.show', 'The claims_never_checked_out_count field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_never_checked_out_count.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.suggest', 'GUI: Suggest claims_never_checked_out_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.suggest', 'The claims_never_checked_out_count field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_returned_count.show',
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.show', 'GUI: Show claims_returned_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.show', 'The claims_returned_count field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_returned_count.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.suggest', 'GUI: Suggest claims_returned_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.suggest', 'The claims_returned_count field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.day_phone.example',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.example', 'GUI: Example for day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.example', 'The Example for validation on the day_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.day_phone.regex',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.regex', 'GUI: Regex for day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.regex', 'The Regular Expression for validation on the day_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.day_phone.require',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.require', 'GUI: Require day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.require', 'The day_phone field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.day_phone.show',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.show', 'GUI: Show day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.show', 'The day_phone field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.day_phone.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.suggest', 'GUI: Suggest day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.suggest', 'The day_phone field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.calendar',
+    oils_i18n_gettext('ui.patron.edit.au.dob.calendar', 'GUI: Show calendar widget for dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.calendar', 'If set the calendar widget will appear when editing the dob field on the patron registration form.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.require',
+    oils_i18n_gettext('ui.patron.edit.au.dob.require', 'GUI: Require dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.require', 'The dob field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.show',
+    oils_i18n_gettext('ui.patron.edit.au.dob.show', 'GUI: Show dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.show', 'The dob field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.dob.suggest', 'GUI: Suggest dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.suggest', 'The dob field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.email.example',
+    oils_i18n_gettext('ui.patron.edit.au.email.example', 'GUI: Example for email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.example', 'The Example for validation on the email field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.email.regex',
+    oils_i18n_gettext('ui.patron.edit.au.email.regex', 'GUI: Regex for email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.regex', 'The Regular Expression for validation on the email field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.email.require',
+    oils_i18n_gettext('ui.patron.edit.au.email.require', 'GUI: Require email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.require', 'The email field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.email.show',
+    oils_i18n_gettext('ui.patron.edit.au.email.show', 'GUI: Show email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.show', 'The email field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.email.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.email.suggest', 'GUI: Suggest email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.suggest', 'The email field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.evening_phone.example',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.example', 'GUI: Example for evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.example', 'The Example for validation on the evening_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.evening_phone.regex',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.regex', 'GUI: Regex for evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.regex', 'The Regular Expression for validation on the evening_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.evening_phone.require',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.require', 'GUI: Require evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.require', 'The evening_phone field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.evening_phone.show',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.show', 'GUI: Show evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.show', 'The evening_phone field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.evening_phone.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.suggest', 'GUI: Suggest evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.suggest', 'The evening_phone field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value.show',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.show', 'GUI: Show ident_value field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.show', 'The ident_value field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.suggest', 'GUI: Suggest ident_value field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.suggest', 'The ident_value field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value2.show',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.show', 'GUI: Show ident_value2 field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.show', 'The ident_value2 field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value2.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.suggest', 'GUI: Suggest ident_value2 field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.suggest', 'The ident_value2 field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.juvenile.show',
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.show', 'GUI: Show juvenile field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.show', 'The juvenile field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.juvenile.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.suggest', 'GUI: Suggest juvenile field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.suggest', 'The juvenile field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.master_account.show',
+    oils_i18n_gettext('ui.patron.edit.au.master_account.show', 'GUI: Show master_account field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.master_account.show', 'The master_account field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.master_account.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.master_account.suggest', 'GUI: Suggest master_account field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.master_account.suggest', 'The master_account field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.other_phone.example',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.example', 'GUI: Example for other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.example', 'The Example for validation on the other_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.other_phone.regex',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.regex', 'GUI: Regex for other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.regex', 'The Regular Expression for validation on the other_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.other_phone.require',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.require', 'GUI: Require other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.require', 'The other_phone field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.other_phone.show',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.show', 'GUI: Show other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.show', 'The other_phone field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.other_phone.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.suggest', 'GUI: Suggest other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.suggest', 'The other_phone field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.second_given_name.show',
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.show', 'GUI: Show second_given_name field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.show', 'The second_given_name field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.second_given_name.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.suggest', 'GUI: Suggest second_given_name field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.suggest', 'The second_given_name field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.suffix.show',
+    oils_i18n_gettext('ui.patron.edit.au.suffix.show', 'GUI: Show suffix field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.suffix.show', 'The suffix field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.suffix.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.suffix.suggest', 'GUI: Suggest suffix field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.suffix.suggest', 'The suffix field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.aua.county.require',
+    oils_i18n_gettext('ui.patron.edit.aua.county.require', 'GUI: Require county field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.aua.county.require', 'The county field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.aua.post_code.example',
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.example', 'GUI: Example for post_code field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.example', 'The Example for validation on the post_code field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.aua.post_code.regex',
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.regex', 'GUI: Regex for post_code field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.regex', 'The Regular Expression for validation on the post_code field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.default_suggested',
+    oils_i18n_gettext('ui.patron.edit.default_suggested', 'GUI: Default showing suggested patron registration fields', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.default_suggested', 'Instead of All fields, show just suggested fields in patron registration by default.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.phone.example',
+    oils_i18n_gettext('ui.patron.edit.phone.example', 'GUI: Example for phone fields on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.phone.example', 'The Example for validation on phone fields in patron registration. Applies to all phone fields without their own setting.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.phone.regex',
+    oils_i18n_gettext('ui.patron.edit.phone.regex', 'GUI: Regex for phone fields on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.phone.regex', 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting.', 'coust', 'description'),
+    'string');
+
 -- *** Has to go below coust definition to satisfy referential integrity ***
 -- In booking, elbow room defines:
 --  a) how far in the future you must make a reservation on a given item if
@@ -3817,6 +3455,587 @@ 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', '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');
 
+-- record attributes
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('alph','Alph','Alph');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('audience','Audn','Audn');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('bib_level','BLvl','BLvl');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('biog','Biog','Biog');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('conf','Conf','Conf');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('control_type','Ctrl','Ctrl');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('ctry','Ctry','Ctry');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('date1','Date1','Date1');
+INSERT INTO config.record_attr_definition (name,label,fixed_field,sorter,filter) values ('pubdate','Pub Date','Date1',TRUE,FALSE);
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('date2','Date2','Date2');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('cat_form','Desc','Desc');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('pub_status','DtSt','DtSt');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('enc_level','ELvl','ELvl');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('fest','Fest','Fest');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('item_form','Form','Form');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('gpub','GPub','GPub');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('ills','Ills','Ills');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('indx','Indx','Indx');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('item_lang','Lang','Lang');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('lit_form','LitF','LitF');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('mrec','MRec','MRec');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('ff_sl','S/L','S/L');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('type_mat','TMat','TMat');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('item_type','Type','Type');
+INSERT INTO config.record_attr_definition (name,label,phys_char_sf) values ('vr_format','Videorecording format',72);
+INSERT INTO config.record_attr_definition (name,label,sorter,filter,tag) values ('titlesort','Title',TRUE,FALSE,'tnf');
+INSERT INTO config.record_attr_definition (name,label,sorter,filter,tag) values ('authorsort','Author',TRUE,FALSE,'1%');
+
+-- TO-DO: Auto-generate these values from CLDR
+-- XXX These are the values used in MARC records ... does that match CLDR, including deprecated languages?
+INSERT INTO config.coded_value_map (id, ctype, code, value) VALUES
+    (1, 'item_lang', 'aar', oils_i18n_gettext('aar', 'Afar', 'ccvm', 'value')),
+    (2, 'item_lang', 'abk', oils_i18n_gettext('abk', 'Abkhaz', 'ccvm', 'value')),
+    (3, 'item_lang', 'ace', oils_i18n_gettext('ace', 'Achinese', 'ccvm', 'value')),
+    (4, 'item_lang', 'ach', oils_i18n_gettext('ach', 'Acoli', 'ccvm', 'value')),
+    (5, 'item_lang', 'ada', oils_i18n_gettext('ada', 'Adangme', 'ccvm', 'value')),
+    (6, 'item_lang', 'ady', oils_i18n_gettext('ady', 'Adygei', 'ccvm', 'value')),
+    (7, 'item_lang', 'afa', oils_i18n_gettext('afa', 'Afroasiatic (Other)', 'ccvm', 'value')),
+    (8, 'item_lang', 'afh', oils_i18n_gettext('afh', 'Afrihili (Artificial language)', 'ccvm', 'value')),
+    (9, 'item_lang', 'afr', oils_i18n_gettext('afr', 'Afrikaans', 'ccvm', 'value')),
+    (10, 'item_lang', '-ajm', oils_i18n_gettext('-ajm', 'Aljamía', 'ccvm', 'value')),
+    (11, 'item_lang', 'aka', oils_i18n_gettext('aka', 'Akan', 'ccvm', 'value')),
+    (12, 'item_lang', 'akk', oils_i18n_gettext('akk', 'Akkadian', 'ccvm', 'value')),
+    (13, 'item_lang', 'alb', oils_i18n_gettext('alb', 'Albanian', 'ccvm', 'value')),
+    (14, 'item_lang', 'ale', oils_i18n_gettext('ale', 'Aleut', 'ccvm', 'value')),
+    (15, 'item_lang', 'alg', oils_i18n_gettext('alg', 'Algonquian (Other)', 'ccvm', 'value')),
+    (16, 'item_lang', 'amh', oils_i18n_gettext('amh', 'Amharic', 'ccvm', 'value')),
+    (17, 'item_lang', 'ang', oils_i18n_gettext('ang', 'English, Old (ca. 450-1100)', 'ccvm', 'value')),
+    (18, 'item_lang', 'apa', oils_i18n_gettext('apa', 'Apache languages', 'ccvm', 'value')),
+    (19, 'item_lang', 'ara', oils_i18n_gettext('ara', 'Arabic', 'ccvm', 'value')),
+    (20, 'item_lang', 'arc', oils_i18n_gettext('arc', 'Aramaic', 'ccvm', 'value')),
+    (21, 'item_lang', 'arg', oils_i18n_gettext('arg', 'Aragonese Spanish', 'ccvm', 'value')),
+    (22, 'item_lang', 'arm', oils_i18n_gettext('arm', 'Armenian', 'ccvm', 'value')),
+    (23, 'item_lang', 'arn', oils_i18n_gettext('arn', 'Mapuche', 'ccvm', 'value')),
+    (24, 'item_lang', 'arp', oils_i18n_gettext('arp', 'Arapaho', 'ccvm', 'value')),
+    (25, 'item_lang', 'art', oils_i18n_gettext('art', 'Artificial (Other)', 'ccvm', 'value')),
+    (26, 'item_lang', 'arw', oils_i18n_gettext('arw', 'Arawak', 'ccvm', 'value')),
+    (27, 'item_lang', 'asm', oils_i18n_gettext('asm', 'Assamese', 'ccvm', 'value')),
+    (28, 'item_lang', 'ast', oils_i18n_gettext('ast', 'Bable', 'ccvm', 'value')),
+    (29, 'item_lang', 'ath', oils_i18n_gettext('ath', 'Athapascan (Other)', 'ccvm', 'value')),
+    (30, 'item_lang', 'aus', oils_i18n_gettext('aus', 'Australian languages', 'ccvm', 'value')),
+    (31, 'item_lang', 'ava', oils_i18n_gettext('ava', 'Avaric', 'ccvm', 'value')),
+    (32, 'item_lang', 'ave', oils_i18n_gettext('ave', 'Avestan', 'ccvm', 'value')),
+    (33, 'item_lang', 'awa', oils_i18n_gettext('awa', 'Awadhi', 'ccvm', 'value')),
+    (34, 'item_lang', 'aym', oils_i18n_gettext('aym', 'Aymara', 'ccvm', 'value')),
+    (35, 'item_lang', 'aze', oils_i18n_gettext('aze', 'Azerbaijani', 'ccvm', 'value')),
+    (36, 'item_lang', 'bad', oils_i18n_gettext('bad', 'Banda', 'ccvm', 'value')),
+    (37, 'item_lang', 'bai', oils_i18n_gettext('bai', 'Bamileke languages', 'ccvm', 'value')),
+    (38, 'item_lang', 'bak', oils_i18n_gettext('bak', 'Bashkir', 'ccvm', 'value')),
+    (39, 'item_lang', 'bal', oils_i18n_gettext('bal', 'Baluchi', 'ccvm', 'value')),
+    (40, 'item_lang', 'bam', oils_i18n_gettext('40', 'Bambara', 'ccvm', 'value')),
+    (41, 'item_lang', 'ban', oils_i18n_gettext('41', 'Balinese', 'ccvm', 'value')),
+    (42, 'item_lang', 'baq', oils_i18n_gettext('42', 'Basque', 'ccvm', 'value')),
+    (43, 'item_lang', 'bas', oils_i18n_gettext('43', 'Basa', 'ccvm', 'value')),
+    (44, 'item_lang', 'bat', oils_i18n_gettext('44', 'Baltic (Other)', 'ccvm', 'value')),
+    (45, 'item_lang', 'bej', oils_i18n_gettext('45', 'Beja', 'ccvm', 'value')),
+    (46, 'item_lang', 'bel', oils_i18n_gettext('46', 'Belarusian', 'ccvm', 'value')),
+    (47, 'item_lang', 'bem', oils_i18n_gettext('47', 'Bemba', 'ccvm', 'value')),
+    (48, 'item_lang', 'ben', oils_i18n_gettext('48', 'Bengali', 'ccvm', 'value')),
+    (49, 'item_lang', 'ber', oils_i18n_gettext('49', 'Berber (Other)', 'ccvm', 'value')),
+    (50, 'item_lang', 'bho', oils_i18n_gettext('50', 'Bhojpuri', 'ccvm', 'value')),
+    (51, 'item_lang', 'bih', oils_i18n_gettext('51', 'Bihari', 'ccvm', 'value')),
+    (52, 'item_lang', 'bik', oils_i18n_gettext('52', 'Bikol', 'ccvm', 'value')),
+    (53, 'item_lang', 'bin', oils_i18n_gettext('53', 'Edo', 'ccvm', 'value')),
+    (54, 'item_lang', 'bis', oils_i18n_gettext('54', 'Bislama', 'ccvm', 'value')),
+    (55, 'item_lang', 'bla', oils_i18n_gettext('55', 'Siksika', 'ccvm', 'value')),
+    (56, 'item_lang', 'bnt', oils_i18n_gettext('56', 'Bantu (Other)', 'ccvm', 'value')),
+    (57, 'item_lang', 'bos', oils_i18n_gettext('57', 'Bosnian', 'ccvm', 'value')),
+    (58, 'item_lang', 'bra', oils_i18n_gettext('58', 'Braj', 'ccvm', 'value')),
+    (59, 'item_lang', 'bre', oils_i18n_gettext('59', 'Breton', 'ccvm', 'value')),
+    (60, 'item_lang', 'btk', oils_i18n_gettext('60', 'Batak', 'ccvm', 'value')),
+    (61, 'item_lang', 'bua', oils_i18n_gettext('61', 'Buriat', 'ccvm', 'value')),
+    (62, 'item_lang', 'bug', oils_i18n_gettext('62', 'Bugis', 'ccvm', 'value')),
+    (63, 'item_lang', 'bul', oils_i18n_gettext('63', 'Bulgarian', 'ccvm', 'value')),
+    (64, 'item_lang', 'bur', oils_i18n_gettext('64', 'Burmese', 'ccvm', 'value')),
+    (65, 'item_lang', 'cad', oils_i18n_gettext('65', 'Caddo', 'ccvm', 'value')),
+    (66, 'item_lang', 'cai', oils_i18n_gettext('66', 'Central American Indian (Other)', 'ccvm', 'value')),
+    (67, 'item_lang', '-cam', oils_i18n_gettext('67', 'Khmer', 'ccvm', 'value')),
+    (68, 'item_lang', 'car', oils_i18n_gettext('68', 'Carib', 'ccvm', 'value')),
+    (69, 'item_lang', 'cat', oils_i18n_gettext('69', 'Catalan', 'ccvm', 'value')),
+    (70, 'item_lang', 'cau', oils_i18n_gettext('70', 'Caucasian (Other)', 'ccvm', 'value')),
+    (71, 'item_lang', 'ceb', oils_i18n_gettext('71', 'Cebuano', 'ccvm', 'value')),
+    (72, 'item_lang', 'cel', oils_i18n_gettext('72', 'Celtic (Other)', 'ccvm', 'value')),
+    (73, 'item_lang', 'cha', oils_i18n_gettext('73', 'Chamorro', 'ccvm', 'value')),
+    (74, 'item_lang', 'chb', oils_i18n_gettext('74', 'Chibcha', 'ccvm', 'value')),
+    (75, 'item_lang', 'che', oils_i18n_gettext('75', 'Chechen', 'ccvm', 'value')),
+    (76, 'item_lang', 'chg', oils_i18n_gettext('76', 'Chagatai', 'ccvm', 'value')),
+    (77, 'item_lang', 'chi', oils_i18n_gettext('77', 'Chinese', 'ccvm', 'value')),
+    (78, 'item_lang', 'chk', oils_i18n_gettext('78', 'Truk', 'ccvm', 'value')),
+    (79, 'item_lang', 'chm', oils_i18n_gettext('79', 'Mari', 'ccvm', 'value')),
+    (80, 'item_lang', 'chn', oils_i18n_gettext('80', 'Chinook jargon', 'ccvm', 'value')),
+    (81, 'item_lang', 'cho', oils_i18n_gettext('81', 'Choctaw', 'ccvm', 'value')),
+    (82, 'item_lang', 'chp', oils_i18n_gettext('82', 'Chipewyan', 'ccvm', 'value')),
+    (83, 'item_lang', 'chr', oils_i18n_gettext('83', 'Cherokee', 'ccvm', 'value')),
+    (84, 'item_lang', 'chu', oils_i18n_gettext('84', 'Church Slavic', 'ccvm', 'value')),
+    (85, 'item_lang', 'chv', oils_i18n_gettext('85', 'Chuvash', 'ccvm', 'value')),
+    (86, 'item_lang', 'chy', oils_i18n_gettext('86', 'Cheyenne', 'ccvm', 'value')),
+    (87, 'item_lang', 'cmc', oils_i18n_gettext('87', 'Chamic languages', 'ccvm', 'value')),
+    (88, 'item_lang', 'cop', oils_i18n_gettext('88', 'Coptic', 'ccvm', 'value')),
+    (89, 'item_lang', 'cor', oils_i18n_gettext('89', 'Cornish', 'ccvm', 'value')),
+    (90, 'item_lang', 'cos', oils_i18n_gettext('90', 'Corsican', 'ccvm', 'value')),
+    (91, 'item_lang', 'cpe', oils_i18n_gettext('91', 'Creoles and Pidgins, English-based (Other)', 'ccvm', 'value')),
+    (92, 'item_lang', 'cpf', oils_i18n_gettext('92', 'Creoles and Pidgins, French-based (Other)', 'ccvm', 'value')),
+    (93, 'item_lang', 'cpp', oils_i18n_gettext('93', 'Creoles and Pidgins, Portuguese-based (Other)', 'ccvm', 'value')),
+    (94, 'item_lang', 'cre', oils_i18n_gettext('94', 'Cree', 'ccvm', 'value')),
+    (95, 'item_lang', 'crh', oils_i18n_gettext('95', 'Crimean Tatar', 'ccvm', 'value')),
+    (96, 'item_lang', 'crp', oils_i18n_gettext('96', 'Creoles and Pidgins (Other)', 'ccvm', 'value')),
+    (97, 'item_lang', 'cus', oils_i18n_gettext('97', 'Cushitic (Other)', 'ccvm', 'value')),
+    (98, 'item_lang', 'cze', oils_i18n_gettext('98', 'Czech', 'ccvm', 'value')),
+    (99, 'item_lang', 'dak', oils_i18n_gettext('99', 'Dakota', 'ccvm', 'value')),
+    (100, 'item_lang', 'dan', oils_i18n_gettext('100', 'Danish', 'ccvm', 'value')),
+    (101, 'item_lang', 'dar', oils_i18n_gettext('101', 'Dargwa', 'ccvm', 'value')),
+    (102, 'item_lang', 'day', oils_i18n_gettext('102', 'Dayak', 'ccvm', 'value')),
+    (103, 'item_lang', 'del', oils_i18n_gettext('103', 'Delaware', 'ccvm', 'value')),
+    (104, 'item_lang', 'den', oils_i18n_gettext('104', 'Slave', 'ccvm', 'value')),
+    (105, 'item_lang', 'dgr', oils_i18n_gettext('105', 'Dogrib', 'ccvm', 'value')),
+    (106, 'item_lang', 'din', oils_i18n_gettext('106', 'Dinka', 'ccvm', 'value')),
+    (107, 'item_lang', 'div', oils_i18n_gettext('107', 'Divehi', 'ccvm', 'value')),
+    (108, 'item_lang', 'doi', oils_i18n_gettext('108', 'Dogri', 'ccvm', 'value')),
+    (109, 'item_lang', 'dra', oils_i18n_gettext('109', 'Dravidian (Other)', 'ccvm', 'value')),
+    (110, 'item_lang', 'dua', oils_i18n_gettext('110', 'Duala', 'ccvm', 'value')),
+    (111, 'item_lang', 'dum', oils_i18n_gettext('111', 'Dutch, Middle (ca. 1050-1350)', 'ccvm', 'value')),
+    (112, 'item_lang', 'dut', oils_i18n_gettext('112', 'Dutch', 'ccvm', 'value')),
+    (113, 'item_lang', 'dyu', oils_i18n_gettext('113', 'Dyula', 'ccvm', 'value')),
+    (114, 'item_lang', 'dzo', oils_i18n_gettext('114', 'Dzongkha', 'ccvm', 'value')),
+    (115, 'item_lang', 'efi', oils_i18n_gettext('115', 'Efik', 'ccvm', 'value')),
+    (116, 'item_lang', 'egy', oils_i18n_gettext('116', 'Egyptian', 'ccvm', 'value')),
+    (117, 'item_lang', 'eka', oils_i18n_gettext('117', 'Ekajuk', 'ccvm', 'value')),
+    (118, 'item_lang', 'elx', oils_i18n_gettext('118', 'Elamite', 'ccvm', 'value')),
+    (119, 'item_lang', 'eng', oils_i18n_gettext('119', 'English', 'ccvm', 'value')),
+    (120, 'item_lang', 'enm', oils_i18n_gettext('120', 'English, Middle (1100-1500)', 'ccvm', 'value')),
+    (121, 'item_lang', 'epo', oils_i18n_gettext('121', 'Esperanto', 'ccvm', 'value')),
+    (122, 'item_lang', '-esk', oils_i18n_gettext('122', 'Eskimo languages', 'ccvm', 'value')),
+    (123, 'item_lang', '-esp', oils_i18n_gettext('123', 'Esperanto', 'ccvm', 'value')),
+    (124, 'item_lang', 'est', oils_i18n_gettext('124', 'Estonian', 'ccvm', 'value')),
+    (125, 'item_lang', '-eth', oils_i18n_gettext('125', 'Ethiopic', 'ccvm', 'value')),
+    (126, 'item_lang', 'ewe', oils_i18n_gettext('126', 'Ewe', 'ccvm', 'value')),
+    (127, 'item_lang', 'ewo', oils_i18n_gettext('127', 'Ewondo', 'ccvm', 'value')),
+    (128, 'item_lang', 'fan', oils_i18n_gettext('128', 'Fang', 'ccvm', 'value')),
+    (129, 'item_lang', 'fao', oils_i18n_gettext('129', 'Faroese', 'ccvm', 'value')),
+    (130, 'item_lang', '-far', oils_i18n_gettext('130', 'Faroese', 'ccvm', 'value')),
+    (131, 'item_lang', 'fat', oils_i18n_gettext('131', 'Fanti', 'ccvm', 'value')),
+    (132, 'item_lang', 'fij', oils_i18n_gettext('132', 'Fijian', 'ccvm', 'value')),
+    (133, 'item_lang', 'fin', oils_i18n_gettext('133', 'Finnish', 'ccvm', 'value')),
+    (134, 'item_lang', 'fiu', oils_i18n_gettext('134', 'Finno-Ugrian (Other)', 'ccvm', 'value')),
+    (135, 'item_lang', 'fon', oils_i18n_gettext('135', 'Fon', 'ccvm', 'value')),
+    (136, 'item_lang', 'fre', oils_i18n_gettext('136', 'French', 'ccvm', 'value')),
+    (137, 'item_lang', '-fri', oils_i18n_gettext('137', 'Frisian', 'ccvm', 'value')),
+    (138, 'item_lang', 'frm', oils_i18n_gettext('138', 'French, Middle (ca. 1400-1600)', 'ccvm', 'value')),
+    (139, 'item_lang', 'fro', oils_i18n_gettext('139', 'French, Old (ca. 842-1400)', 'ccvm', 'value')),
+    (140, 'item_lang', 'fry', oils_i18n_gettext('140', 'Frisian', 'ccvm', 'value')),
+    (141, 'item_lang', 'ful', oils_i18n_gettext('141', 'Fula', 'ccvm', 'value')),
+    (142, 'item_lang', 'fur', oils_i18n_gettext('142', 'Friulian', 'ccvm', 'value')),
+    (143, 'item_lang', 'gaa', oils_i18n_gettext('143', 'Gã', 'ccvm', 'value')),
+    (144, 'item_lang', '-gae', oils_i18n_gettext('144', 'Scottish Gaelic', 'ccvm', 'value')),
+    (145, 'item_lang', '-gag', oils_i18n_gettext('145', 'Galician', 'ccvm', 'value')),
+    (146, 'item_lang', '-gal', oils_i18n_gettext('146', 'Oromo', 'ccvm', 'value')),
+    (147, 'item_lang', 'gay', oils_i18n_gettext('147', 'Gayo', 'ccvm', 'value')),
+    (148, 'item_lang', 'gba', oils_i18n_gettext('148', 'Gbaya', 'ccvm', 'value')),
+    (149, 'item_lang', 'gem', oils_i18n_gettext('149', 'Germanic (Other)', 'ccvm', 'value')),
+    (150, 'item_lang', 'geo', oils_i18n_gettext('150', 'Georgian', 'ccvm', 'value')),
+    (151, 'item_lang', 'ger', oils_i18n_gettext('151', 'German', 'ccvm', 'value')),
+    (152, 'item_lang', 'gez', oils_i18n_gettext('152', 'Ethiopic', 'ccvm', 'value')),
+    (153, 'item_lang', 'gil', oils_i18n_gettext('153', 'Gilbertese', 'ccvm', 'value')),
+    (154, 'item_lang', 'gla', oils_i18n_gettext('154', 'Scottish Gaelic', 'ccvm', 'value')),
+    (155, 'item_lang', 'gle', oils_i18n_gettext('155', 'Irish', 'ccvm', 'value')),
+    (156, 'item_lang', 'glg', oils_i18n_gettext('156', 'Galician', 'ccvm', 'value')),
+    (157, 'item_lang', 'glv', oils_i18n_gettext('157', 'Manx', 'ccvm', 'value')),
+    (158, 'item_lang', 'gmh', oils_i18n_gettext('158', 'German, Middle High (ca. 1050-1500)', 'ccvm', 'value')),
+    (159, 'item_lang', 'goh', oils_i18n_gettext('159', 'German, Old High (ca. 750-1050)', 'ccvm', 'value')),
+    (160, 'item_lang', 'gon', oils_i18n_gettext('160', 'Gondi', 'ccvm', 'value')),
+    (161, 'item_lang', 'gor', oils_i18n_gettext('161', 'Gorontalo', 'ccvm', 'value')),
+    (162, 'item_lang', 'got', oils_i18n_gettext('162', 'Gothic', 'ccvm', 'value')),
+    (163, 'item_lang', 'grb', oils_i18n_gettext('163', 'Grebo', 'ccvm', 'value')),
+    (164, 'item_lang', 'grc', oils_i18n_gettext('164', 'Greek, Ancient (to 1453)', 'ccvm', 'value')),
+    (165, 'item_lang', 'gre', oils_i18n_gettext('165', 'Greek, Modern (1453- )', 'ccvm', 'value')),
+    (166, 'item_lang', 'grn', oils_i18n_gettext('166', 'Guarani', 'ccvm', 'value')),
+    (167, 'item_lang', '-gua', oils_i18n_gettext('167', 'Guarani', 'ccvm', 'value')),
+    (168, 'item_lang', 'guj', oils_i18n_gettext('168', 'Gujarati', 'ccvm', 'value')),
+    (169, 'item_lang', 'gwi', oils_i18n_gettext('169', 'Gwich', 'ccvm', 'value''in')),
+    (170, 'item_lang', 'hai', oils_i18n_gettext('170', 'Haida', 'ccvm', 'value')),
+    (171, 'item_lang', 'hat', oils_i18n_gettext('171', 'Haitian French Creole', 'ccvm', 'value')),
+    (172, 'item_lang', 'hau', oils_i18n_gettext('172', 'Hausa', 'ccvm', 'value')),
+    (173, 'item_lang', 'haw', oils_i18n_gettext('173', 'Hawaiian', 'ccvm', 'value')),
+    (174, 'item_lang', 'heb', oils_i18n_gettext('174', 'Hebrew', 'ccvm', 'value')),
+    (175, 'item_lang', 'her', oils_i18n_gettext('175', 'Herero', 'ccvm', 'value')),
+    (176, 'item_lang', 'hil', oils_i18n_gettext('176', 'Hiligaynon', 'ccvm', 'value')),
+    (177, 'item_lang', 'him', oils_i18n_gettext('177', 'Himachali', 'ccvm', 'value')),
+    (178, 'item_lang', 'hin', oils_i18n_gettext('178', 'Hindi', 'ccvm', 'value')),
+    (179, 'item_lang', 'hit', oils_i18n_gettext('179', 'Hittite', 'ccvm', 'value')),
+    (180, 'item_lang', 'hmn', oils_i18n_gettext('180', 'Hmong', 'ccvm', 'value')),
+    (181, 'item_lang', 'hmo', oils_i18n_gettext('181', 'Hiri Motu', 'ccvm', 'value')),
+    (182, 'item_lang', 'hun', oils_i18n_gettext('182', 'Hungarian', 'ccvm', 'value')),
+    (183, 'item_lang', 'hup', oils_i18n_gettext('183', 'Hupa', 'ccvm', 'value')),
+    (184, 'item_lang', 'iba', oils_i18n_gettext('184', 'Iban', 'ccvm', 'value')),
+    (185, 'item_lang', 'ibo', oils_i18n_gettext('185', 'Igbo', 'ccvm', 'value')),
+    (186, 'item_lang', 'ice', oils_i18n_gettext('186', 'Icelandic', 'ccvm', 'value')),
+    (187, 'item_lang', 'ido', oils_i18n_gettext('187', 'Ido', 'ccvm', 'value')),
+    (188, 'item_lang', 'iii', oils_i18n_gettext('188', 'Sichuan Yi', 'ccvm', 'value')),
+    (189, 'item_lang', 'ijo', oils_i18n_gettext('189', 'Ijo', 'ccvm', 'value')),
+    (190, 'item_lang', 'iku', oils_i18n_gettext('190', 'Inuktitut', 'ccvm', 'value')),
+    (191, 'item_lang', 'ile', oils_i18n_gettext('191', 'Interlingue', 'ccvm', 'value')),
+    (192, 'item_lang', 'ilo', oils_i18n_gettext('192', 'Iloko', 'ccvm', 'value')),
+    (193, 'item_lang', 'ina', oils_i18n_gettext('193', 'Interlingua (International Auxiliary Language Association)', 'ccvm', 'value')),
+    (194, 'item_lang', 'inc', oils_i18n_gettext('194', 'Indic (Other)', 'ccvm', 'value')),
+    (195, 'item_lang', 'ind', oils_i18n_gettext('195', 'Indonesian', 'ccvm', 'value')),
+    (196, 'item_lang', 'ine', oils_i18n_gettext('196', 'Indo-European (Other)', 'ccvm', 'value')),
+    (197, 'item_lang', 'inh', oils_i18n_gettext('197', 'Ingush', 'ccvm', 'value')),
+    (198, 'item_lang', '-int', oils_i18n_gettext('198', 'Interlingua (International Auxiliary Language Association)', 'ccvm', 'value')),
+    (199, 'item_lang', 'ipk', oils_i18n_gettext('199', 'Inupiaq', 'ccvm', 'value')),
+    (200, 'item_lang', 'ira', oils_i18n_gettext('200', 'Iranian (Other)', 'ccvm', 'value')),
+    (201, 'item_lang', '-iri', oils_i18n_gettext('201', 'Irish', 'ccvm', 'value')),
+    (202, 'item_lang', 'iro', oils_i18n_gettext('202', 'Iroquoian (Other)', 'ccvm', 'value')),
+    (203, 'item_lang', 'ita', oils_i18n_gettext('203', 'Italian', 'ccvm', 'value')),
+    (204, 'item_lang', 'jav', oils_i18n_gettext('204', 'Javanese', 'ccvm', 'value')),
+    (205, 'item_lang', 'jpn', oils_i18n_gettext('205', 'Japanese', 'ccvm', 'value')),
+    (206, 'item_lang', 'jpr', oils_i18n_gettext('206', 'Judeo-Persian', 'ccvm', 'value')),
+    (207, 'item_lang', 'jrb', oils_i18n_gettext('207', 'Judeo-Arabic', 'ccvm', 'value')),
+    (208, 'item_lang', 'kaa', oils_i18n_gettext('208', 'Kara-Kalpak', 'ccvm', 'value')),
+    (209, 'item_lang', 'kab', oils_i18n_gettext('209', 'Kabyle', 'ccvm', 'value')),
+    (210, 'item_lang', 'kac', oils_i18n_gettext('210', 'Kachin', 'ccvm', 'value')),
+    (211, 'item_lang', 'kal', oils_i18n_gettext('211', 'Kalâtdlisut', 'ccvm', 'value')),
+    (212, 'item_lang', 'kam', oils_i18n_gettext('212', 'Kamba', 'ccvm', 'value')),
+    (213, 'item_lang', 'kan', oils_i18n_gettext('213', 'Kannada', 'ccvm', 'value')),
+    (214, 'item_lang', 'kar', oils_i18n_gettext('214', 'Karen', 'ccvm', 'value')),
+    (215, 'item_lang', 'kas', oils_i18n_gettext('215', 'Kashmiri', 'ccvm', 'value')),
+    (216, 'item_lang', 'kau', oils_i18n_gettext('216', 'Kanuri', 'ccvm', 'value')),
+    (217, 'item_lang', 'kaw', oils_i18n_gettext('217', 'Kawi', 'ccvm', 'value')),
+    (218, 'item_lang', 'kaz', oils_i18n_gettext('218', 'Kazakh', 'ccvm', 'value')),
+    (219, 'item_lang', 'kbd', oils_i18n_gettext('219', 'Kabardian', 'ccvm', 'value')),
+    (220, 'item_lang', 'kha', oils_i18n_gettext('220', 'Khasi', 'ccvm', 'value')),
+    (221, 'item_lang', 'khi', oils_i18n_gettext('221', 'Khoisan (Other)', 'ccvm', 'value')),
+    (222, 'item_lang', 'khm', oils_i18n_gettext('222', 'Khmer', 'ccvm', 'value')),
+    (223, 'item_lang', 'kho', oils_i18n_gettext('223', 'Khotanese', 'ccvm', 'value')),
+    (224, 'item_lang', 'kik', oils_i18n_gettext('224', 'Kikuyu', 'ccvm', 'value')),
+    (225, 'item_lang', 'kin', oils_i18n_gettext('225', 'Kinyarwanda', 'ccvm', 'value')),
+    (226, 'item_lang', 'kir', oils_i18n_gettext('226', 'Kyrgyz', 'ccvm', 'value')),
+    (227, 'item_lang', 'kmb', oils_i18n_gettext('227', 'Kimbundu', 'ccvm', 'value')),
+    (228, 'item_lang', 'kok', oils_i18n_gettext('228', 'Konkani', 'ccvm', 'value')),
+    (229, 'item_lang', 'kom', oils_i18n_gettext('229', 'Komi', 'ccvm', 'value')),
+    (230, 'item_lang', 'kon', oils_i18n_gettext('230', 'Kongo', 'ccvm', 'value')),
+    (231, 'item_lang', 'kor', oils_i18n_gettext('231', 'Korean', 'ccvm', 'value')),
+    (232, 'item_lang', 'kos', oils_i18n_gettext('232', 'Kusaie', 'ccvm', 'value')),
+    (233, 'item_lang', 'kpe', oils_i18n_gettext('233', 'Kpelle', 'ccvm', 'value')),
+    (234, 'item_lang', 'kro', oils_i18n_gettext('234', 'Kru', 'ccvm', 'value')),
+    (235, 'item_lang', 'kru', oils_i18n_gettext('235', 'Kurukh', 'ccvm', 'value')),
+    (236, 'item_lang', 'kua', oils_i18n_gettext('236', 'Kuanyama', 'ccvm', 'value')),
+    (237, 'item_lang', 'kum', oils_i18n_gettext('237', 'Kumyk', 'ccvm', 'value')),
+    (238, 'item_lang', 'kur', oils_i18n_gettext('238', 'Kurdish', 'ccvm', 'value')),
+    (239, 'item_lang', '-kus', oils_i18n_gettext('239', 'Kusaie', 'ccvm', 'value')),
+    (240, 'item_lang', 'kut', oils_i18n_gettext('240', 'Kutenai', 'ccvm', 'value')),
+    (241, 'item_lang', 'lad', oils_i18n_gettext('241', 'Ladino', 'ccvm', 'value')),
+    (242, 'item_lang', 'lah', oils_i18n_gettext('242', 'Lahnda', 'ccvm', 'value')),
+    (243, 'item_lang', 'lam', oils_i18n_gettext('243', 'Lamba', 'ccvm', 'value')),
+    (244, 'item_lang', '-lan', oils_i18n_gettext('244', 'Occitan (post-1500)', 'ccvm', 'value')),
+    (245, 'item_lang', 'lao', oils_i18n_gettext('245', 'Lao', 'ccvm', 'value')),
+    (246, 'item_lang', '-lap', oils_i18n_gettext('246', 'Sami', 'ccvm', 'value')),
+    (247, 'item_lang', 'lat', oils_i18n_gettext('247', 'Latin', 'ccvm', 'value')),
+    (248, 'item_lang', 'lav', oils_i18n_gettext('248', 'Latvian', 'ccvm', 'value')),
+    (249, 'item_lang', 'lez', oils_i18n_gettext('249', 'Lezgian', 'ccvm', 'value')),
+    (250, 'item_lang', 'lim', oils_i18n_gettext('250', 'Limburgish', 'ccvm', 'value')),
+    (251, 'item_lang', 'lin', oils_i18n_gettext('251', 'Lingala', 'ccvm', 'value')),
+    (252, 'item_lang', 'lit', oils_i18n_gettext('252', 'Lithuanian', 'ccvm', 'value')),
+    (253, 'item_lang', 'lol', oils_i18n_gettext('253', 'Mongo-Nkundu', 'ccvm', 'value')),
+    (254, 'item_lang', 'loz', oils_i18n_gettext('254', 'Lozi', 'ccvm', 'value')),
+    (255, 'item_lang', 'ltz', oils_i18n_gettext('255', 'Letzeburgesch', 'ccvm', 'value')),
+    (256, 'item_lang', 'lua', oils_i18n_gettext('256', 'Luba-Lulua', 'ccvm', 'value')),
+    (257, 'item_lang', 'lub', oils_i18n_gettext('257', 'Luba-Katanga', 'ccvm', 'value')),
+    (258, 'item_lang', 'lug', oils_i18n_gettext('258', 'Ganda', 'ccvm', 'value')),
+    (259, 'item_lang', 'lui', oils_i18n_gettext('259', 'Luiseño', 'ccvm', 'value')),
+    (260, 'item_lang', 'lun', oils_i18n_gettext('260', 'Lunda', 'ccvm', 'value')),
+    (261, 'item_lang', 'luo', oils_i18n_gettext('261', 'Luo (Kenya and Tanzania)', 'ccvm', 'value')),
+    (262, 'item_lang', 'lus', oils_i18n_gettext('262', 'Lushai', 'ccvm', 'value')),
+    (263, 'item_lang', 'mac', oils_i18n_gettext('263', 'Macedonian', 'ccvm', 'value')),
+    (264, 'item_lang', 'mad', oils_i18n_gettext('264', 'Madurese', 'ccvm', 'value')),
+    (265, 'item_lang', 'mag', oils_i18n_gettext('265', 'Magahi', 'ccvm', 'value')),
+    (266, 'item_lang', 'mah', oils_i18n_gettext('266', 'Marshallese', 'ccvm', 'value')),
+    (267, 'item_lang', 'mai', oils_i18n_gettext('267', 'Maithili', 'ccvm', 'value')),
+    (268, 'item_lang', 'mak', oils_i18n_gettext('268', 'Makasar', 'ccvm', 'value')),
+    (269, 'item_lang', 'mal', oils_i18n_gettext('269', 'Malayalam', 'ccvm', 'value')),
+    (270, 'item_lang', 'man', oils_i18n_gettext('270', 'Mandingo', 'ccvm', 'value')),
+    (271, 'item_lang', 'mao', oils_i18n_gettext('271', 'Maori', 'ccvm', 'value')),
+    (272, 'item_lang', 'map', oils_i18n_gettext('272', 'Austronesian (Other)', 'ccvm', 'value')),
+    (273, 'item_lang', 'mar', oils_i18n_gettext('273', 'Marathi', 'ccvm', 'value')),
+    (274, 'item_lang', 'mas', oils_i18n_gettext('274', 'Masai', 'ccvm', 'value')),
+    (275, 'item_lang', '-max', oils_i18n_gettext('275', 'Manx', 'ccvm', 'value')),
+    (276, 'item_lang', 'may', oils_i18n_gettext('276', 'Malay', 'ccvm', 'value')),
+    (277, 'item_lang', 'mdr', oils_i18n_gettext('277', 'Mandar', 'ccvm', 'value')),
+    (278, 'item_lang', 'men', oils_i18n_gettext('278', 'Mende', 'ccvm', 'value')),
+    (279, 'item_lang', 'mga', oils_i18n_gettext('279', 'Irish, Middle (ca. 1100-1550)', 'ccvm', 'value')),
+    (280, 'item_lang', 'mic', oils_i18n_gettext('280', 'Micmac', 'ccvm', 'value')),
+    (281, 'item_lang', 'min', oils_i18n_gettext('281', 'Minangkabau', 'ccvm', 'value')),
+    (282, 'item_lang', 'mis', oils_i18n_gettext('282', 'Miscellaneous languages', 'ccvm', 'value')),
+    (283, 'item_lang', 'mkh', oils_i18n_gettext('283', 'Mon-Khmer (Other)', 'ccvm', 'value')),
+    (284, 'item_lang', '-mla', oils_i18n_gettext('284', 'Malagasy', 'ccvm', 'value')),
+    (285, 'item_lang', 'mlg', oils_i18n_gettext('285', 'Malagasy', 'ccvm', 'value')),
+    (286, 'item_lang', 'mlt', oils_i18n_gettext('286', 'Maltese', 'ccvm', 'value')),
+    (287, 'item_lang', 'mnc', oils_i18n_gettext('287', 'Manchu', 'ccvm', 'value')),
+    (288, 'item_lang', 'mni', oils_i18n_gettext('288', 'Manipuri', 'ccvm', 'value')),
+    (289, 'item_lang', 'mno', oils_i18n_gettext('289', 'Manobo languages', 'ccvm', 'value')),
+    (290, 'item_lang', 'moh', oils_i18n_gettext('290', 'Mohawk', 'ccvm', 'value')),
+    (291, 'item_lang', 'mol', oils_i18n_gettext('291', 'Moldavian', 'ccvm', 'value')),
+    (292, 'item_lang', 'mon', oils_i18n_gettext('292', 'Mongolian', 'ccvm', 'value')),
+    (293, 'item_lang', 'mos', oils_i18n_gettext('293', 'Mooré', 'ccvm', 'value')),
+    (294, 'item_lang', 'mul', oils_i18n_gettext('294', 'Multiple languages', 'ccvm', 'value')),
+    (295, 'item_lang', 'mun', oils_i18n_gettext('295', 'Munda (Other)', 'ccvm', 'value')),
+    (296, 'item_lang', 'mus', oils_i18n_gettext('296', 'Creek', 'ccvm', 'value')),
+    (297, 'item_lang', 'mwr', oils_i18n_gettext('297', 'Marwari', 'ccvm', 'value')),
+    (298, 'item_lang', 'myn', oils_i18n_gettext('298', 'Mayan languages', 'ccvm', 'value')),
+    (299, 'item_lang', 'nah', oils_i18n_gettext('299', 'Nahuatl', 'ccvm', 'value')),
+    (300, 'item_lang', 'nai', oils_i18n_gettext('300', 'North American Indian (Other)', 'ccvm', 'value')),
+    (301, 'item_lang', 'nap', oils_i18n_gettext('301', 'Neapolitan Italian', 'ccvm', 'value')),
+    (302, 'item_lang', 'nau', oils_i18n_gettext('302', 'Nauru', 'ccvm', 'value')),
+    (303, 'item_lang', 'nav', oils_i18n_gettext('303', 'Navajo', 'ccvm', 'value')),
+    (304, 'item_lang', 'nbl', oils_i18n_gettext('304', 'Ndebele (South Africa)', 'ccvm', 'value')),
+    (305, 'item_lang', 'nde', oils_i18n_gettext('305', 'Ndebele (Zimbabwe)  ', 'ccvm', 'value')),
+    (306, 'item_lang', 'ndo', oils_i18n_gettext('306', 'Ndonga', 'ccvm', 'value')),
+    (307, 'item_lang', 'nds', oils_i18n_gettext('307', 'Low German', 'ccvm', 'value')),
+    (308, 'item_lang', 'nep', oils_i18n_gettext('308', 'Nepali', 'ccvm', 'value')),
+    (309, 'item_lang', 'new', oils_i18n_gettext('309', 'Newari', 'ccvm', 'value')),
+    (310, 'item_lang', 'nia', oils_i18n_gettext('310', 'Nias', 'ccvm', 'value')),
+    (311, 'item_lang', 'nic', oils_i18n_gettext('311', 'Niger-Kordofanian (Other)', 'ccvm', 'value')),
+    (312, 'item_lang', 'niu', oils_i18n_gettext('312', 'Niuean', 'ccvm', 'value')),
+    (313, 'item_lang', 'nno', oils_i18n_gettext('313', 'Norwegian (Nynorsk)', 'ccvm', 'value')),
+    (314, 'item_lang', 'nob', oils_i18n_gettext('314', 'Norwegian (BokmÃ¥l)', 'ccvm', 'value')),
+    (315, 'item_lang', 'nog', oils_i18n_gettext('315', 'Nogai', 'ccvm', 'value')),
+    (316, 'item_lang', 'non', oils_i18n_gettext('316', 'Old Norse', 'ccvm', 'value')),
+    (317, 'item_lang', 'nor', oils_i18n_gettext('317', 'Norwegian', 'ccvm', 'value')),
+    (318, 'item_lang', 'nso', oils_i18n_gettext('318', 'Northern Sotho', 'ccvm', 'value')),
+    (319, 'item_lang', 'nub', oils_i18n_gettext('319', 'Nubian languages', 'ccvm', 'value')),
+    (320, 'item_lang', 'nya', oils_i18n_gettext('320', 'Nyanja', 'ccvm', 'value')),
+    (321, 'item_lang', 'nym', oils_i18n_gettext('321', 'Nyamwezi', 'ccvm', 'value')),
+    (322, 'item_lang', 'nyn', oils_i18n_gettext('322', 'Nyankole', 'ccvm', 'value')),
+    (323, 'item_lang', 'nyo', oils_i18n_gettext('323', 'Nyoro', 'ccvm', 'value')),
+    (324, 'item_lang', 'nzi', oils_i18n_gettext('324', 'Nzima', 'ccvm', 'value')),
+    (325, 'item_lang', 'oci', oils_i18n_gettext('325', 'Occitan (post-1500)', 'ccvm', 'value')),
+    (326, 'item_lang', 'oji', oils_i18n_gettext('326', 'Ojibwa', 'ccvm', 'value')),
+    (327, 'item_lang', 'ori', oils_i18n_gettext('327', 'Oriya', 'ccvm', 'value')),
+    (328, 'item_lang', 'orm', oils_i18n_gettext('328', 'Oromo', 'ccvm', 'value')),
+    (329, 'item_lang', 'osa', oils_i18n_gettext('329', 'Osage', 'ccvm', 'value')),
+    (330, 'item_lang', 'oss', oils_i18n_gettext('330', 'Ossetic', 'ccvm', 'value')),
+    (331, 'item_lang', 'ota', oils_i18n_gettext('331', 'Turkish, Ottoman', 'ccvm', 'value')),
+    (332, 'item_lang', 'oto', oils_i18n_gettext('332', 'Otomian languages', 'ccvm', 'value')),
+    (333, 'item_lang', 'paa', oils_i18n_gettext('333', 'Papuan (Other)', 'ccvm', 'value')),
+    (334, 'item_lang', 'pag', oils_i18n_gettext('334', 'Pangasinan', 'ccvm', 'value')),
+    (335, 'item_lang', 'pal', oils_i18n_gettext('335', 'Pahlavi', 'ccvm', 'value')),
+    (336, 'item_lang', 'pam', oils_i18n_gettext('336', 'Pampanga', 'ccvm', 'value')),
+    (337, 'item_lang', 'pan', oils_i18n_gettext('337', 'Panjabi', 'ccvm', 'value')),
+    (338, 'item_lang', 'pap', oils_i18n_gettext('338', 'Papiamento', 'ccvm', 'value')),
+    (339, 'item_lang', 'pau', oils_i18n_gettext('339', 'Palauan', 'ccvm', 'value')),
+    (340, 'item_lang', 'peo', oils_i18n_gettext('340', 'Old Persian (ca. 600-400 B.C.)', 'ccvm', 'value')),
+    (341, 'item_lang', 'per', oils_i18n_gettext('341', 'Persian', 'ccvm', 'value')),
+    (342, 'item_lang', 'phi', oils_i18n_gettext('342', 'Philippine (Other)', 'ccvm', 'value')),
+    (343, 'item_lang', 'phn', oils_i18n_gettext('343', 'Phoenician', 'ccvm', 'value')),
+    (344, 'item_lang', 'pli', oils_i18n_gettext('344', 'Pali', 'ccvm', 'value')),
+    (345, 'item_lang', 'pol', oils_i18n_gettext('345', 'Polish', 'ccvm', 'value')),
+    (346, 'item_lang', 'pon', oils_i18n_gettext('346', 'Ponape', 'ccvm', 'value')),
+    (347, 'item_lang', 'por', oils_i18n_gettext('347', 'Portuguese', 'ccvm', 'value')),
+    (348, 'item_lang', 'pra', oils_i18n_gettext('348', 'Prakrit languages', 'ccvm', 'value')),
+    (349, 'item_lang', 'pro', oils_i18n_gettext('349', 'Provençal (to 1500)', 'ccvm', 'value')),
+    (350, 'item_lang', 'pus', oils_i18n_gettext('350', 'Pushto', 'ccvm', 'value')),
+    (351, 'item_lang', 'que', oils_i18n_gettext('351', 'Quechua', 'ccvm', 'value')),
+    (352, 'item_lang', 'raj', oils_i18n_gettext('352', 'Rajasthani', 'ccvm', 'value')),
+    (353, 'item_lang', 'rap', oils_i18n_gettext('353', 'Rapanui', 'ccvm', 'value')),
+    (354, 'item_lang', 'rar', oils_i18n_gettext('354', 'Rarotongan', 'ccvm', 'value')),
+    (355, 'item_lang', 'roa', oils_i18n_gettext('355', 'Romance (Other)', 'ccvm', 'value')),
+    (356, 'item_lang', 'roh', oils_i18n_gettext('356', 'Raeto-Romance', 'ccvm', 'value')),
+    (357, 'item_lang', 'rom', oils_i18n_gettext('357', 'Romani', 'ccvm', 'value')),
+    (358, 'item_lang', 'rum', oils_i18n_gettext('358', 'Romanian', 'ccvm', 'value')),
+    (359, 'item_lang', 'run', oils_i18n_gettext('359', 'Rundi', 'ccvm', 'value')),
+    (360, 'item_lang', 'rus', oils_i18n_gettext('360', 'Russian', 'ccvm', 'value')),
+    (361, 'item_lang', 'sad', oils_i18n_gettext('361', 'Sandawe', 'ccvm', 'value')),
+    (362, 'item_lang', 'sag', oils_i18n_gettext('362', 'Sango (Ubangi Creole)', 'ccvm', 'value')),
+    (363, 'item_lang', 'sah', oils_i18n_gettext('363', 'Yakut', 'ccvm', 'value')),
+    (364, 'item_lang', 'sai', oils_i18n_gettext('364', 'South American Indian (Other)', 'ccvm', 'value')),
+    (365, 'item_lang', 'sal', oils_i18n_gettext('365', 'Salishan languages', 'ccvm', 'value')),
+    (366, 'item_lang', 'sam', oils_i18n_gettext('366', 'Samaritan Aramaic', 'ccvm', 'value')),
+    (367, 'item_lang', 'san', oils_i18n_gettext('367', 'Sanskrit', 'ccvm', 'value')),
+    (368, 'item_lang', '-sao', oils_i18n_gettext('368', 'Samoan', 'ccvm', 'value')),
+    (369, 'item_lang', 'sas', oils_i18n_gettext('369', 'Sasak', 'ccvm', 'value')),
+    (370, 'item_lang', 'sat', oils_i18n_gettext('370', 'Santali', 'ccvm', 'value')),
+    (371, 'item_lang', 'scc', oils_i18n_gettext('371', 'Serbian', 'ccvm', 'value')),
+    (372, 'item_lang', 'sco', oils_i18n_gettext('372', 'Scots', 'ccvm', 'value')),
+    (373, 'item_lang', 'scr', oils_i18n_gettext('373', 'Croatian', 'ccvm', 'value')),
+    (374, 'item_lang', 'sel', oils_i18n_gettext('374', 'Selkup', 'ccvm', 'value')),
+    (375, 'item_lang', 'sem', oils_i18n_gettext('375', 'Semitic (Other)', 'ccvm', 'value')),
+    (376, 'item_lang', 'sga', oils_i18n_gettext('376', 'Irish, Old (to 1100)', 'ccvm', 'value')),
+    (377, 'item_lang', 'sgn', oils_i18n_gettext('377', 'Sign languages', 'ccvm', 'value')),
+    (378, 'item_lang', 'shn', oils_i18n_gettext('378', 'Shan', 'ccvm', 'value')),
+    (379, 'item_lang', '-sho', oils_i18n_gettext('379', 'Shona', 'ccvm', 'value')),
+    (380, 'item_lang', 'sid', oils_i18n_gettext('380', 'Sidamo', 'ccvm', 'value')),
+    (381, 'item_lang', 'sin', oils_i18n_gettext('381', 'Sinhalese', 'ccvm', 'value')),
+    (382, 'item_lang', 'sio', oils_i18n_gettext('382', 'Siouan (Other)', 'ccvm', 'value')),
+    (383, 'item_lang', 'sit', oils_i18n_gettext('383', 'Sino-Tibetan (Other)', 'ccvm', 'value')),
+    (384, 'item_lang', 'sla', oils_i18n_gettext('384', 'Slavic (Other)', 'ccvm', 'value')),
+    (385, 'item_lang', 'slo', oils_i18n_gettext('385', 'Slovak', 'ccvm', 'value')),
+    (386, 'item_lang', 'slv', oils_i18n_gettext('386', 'Slovenian', 'ccvm', 'value')),
+    (387, 'item_lang', 'sma', oils_i18n_gettext('387', 'Southern Sami', 'ccvm', 'value')),
+    (388, 'item_lang', 'sme', oils_i18n_gettext('388', 'Northern Sami', 'ccvm', 'value')),
+    (389, 'item_lang', 'smi', oils_i18n_gettext('389', 'Sami', 'ccvm', 'value')),
+    (390, 'item_lang', 'smj', oils_i18n_gettext('390', 'Lule Sami', 'ccvm', 'value')),
+    (391, 'item_lang', 'smn', oils_i18n_gettext('391', 'Inari Sami', 'ccvm', 'value')),
+    (392, 'item_lang', 'smo', oils_i18n_gettext('392', 'Samoan', 'ccvm', 'value')),
+    (393, 'item_lang', 'sms', oils_i18n_gettext('393', 'Skolt Sami', 'ccvm', 'value')),
+    (394, 'item_lang', 'sna', oils_i18n_gettext('394', 'Shona', 'ccvm', 'value')),
+    (395, 'item_lang', 'snd', oils_i18n_gettext('395', 'Sindhi', 'ccvm', 'value')),
+    (396, 'item_lang', '-snh', oils_i18n_gettext('396', 'Sinhalese', 'ccvm', 'value')),
+    (397, 'item_lang', 'snk', oils_i18n_gettext('397', 'Soninke', 'ccvm', 'value')),
+    (398, 'item_lang', 'sog', oils_i18n_gettext('398', 'Sogdian', 'ccvm', 'value')),
+    (399, 'item_lang', 'som', oils_i18n_gettext('399', 'Somali', 'ccvm', 'value')),
+    (400, 'item_lang', 'son', oils_i18n_gettext('400', 'Songhai', 'ccvm', 'value')),
+    (401, 'item_lang', 'sot', oils_i18n_gettext('401', 'Sotho', 'ccvm', 'value')),
+    (402, 'item_lang', 'spa', oils_i18n_gettext('402', 'Spanish', 'ccvm', 'value')),
+    (403, 'item_lang', 'srd', oils_i18n_gettext('403', 'Sardinian', 'ccvm', 'value')),
+    (404, 'item_lang', 'srr', oils_i18n_gettext('404', 'Serer', 'ccvm', 'value')),
+    (405, 'item_lang', 'ssa', oils_i18n_gettext('405', 'Nilo-Saharan (Other)', 'ccvm', 'value')),
+    (406, 'item_lang', '-sso', oils_i18n_gettext('406', 'Sotho', 'ccvm', 'value')),
+    (407, 'item_lang', 'ssw', oils_i18n_gettext('407', 'Swazi', 'ccvm', 'value')),
+    (408, 'item_lang', 'suk', oils_i18n_gettext('408', 'Sukuma', 'ccvm', 'value')),
+    (409, 'item_lang', 'sun', oils_i18n_gettext('409', 'Sundanese', 'ccvm', 'value')),
+    (410, 'item_lang', 'sus', oils_i18n_gettext('410', 'Susu', 'ccvm', 'value')),
+    (411, 'item_lang', 'sux', oils_i18n_gettext('411', 'Sumerian', 'ccvm', 'value')),
+    (412, 'item_lang', 'swa', oils_i18n_gettext('412', 'Swahili', 'ccvm', 'value')),
+    (413, 'item_lang', 'swe', oils_i18n_gettext('413', 'Swedish', 'ccvm', 'value')),
+    (414, 'item_lang', '-swz', oils_i18n_gettext('414', 'Swazi', 'ccvm', 'value')),
+    (415, 'item_lang', 'syr', oils_i18n_gettext('415', 'Syriac', 'ccvm', 'value')),
+    (416, 'item_lang', '-tag', oils_i18n_gettext('416', 'Tagalog', 'ccvm', 'value')),
+    (417, 'item_lang', 'tah', oils_i18n_gettext('417', 'Tahitian', 'ccvm', 'value')),
+    (418, 'item_lang', 'tai', oils_i18n_gettext('418', 'Tai (Other)', 'ccvm', 'value')),
+    (419, 'item_lang', '-taj', oils_i18n_gettext('419', 'Tajik', 'ccvm', 'value')),
+    (420, 'item_lang', 'tam', oils_i18n_gettext('420', 'Tamil', 'ccvm', 'value')),
+    (421, 'item_lang', '-tar', oils_i18n_gettext('421', 'Tatar', 'ccvm', 'value')),
+    (422, 'item_lang', 'tat', oils_i18n_gettext('422', 'Tatar', 'ccvm', 'value')),
+    (423, 'item_lang', 'tel', oils_i18n_gettext('423', 'Telugu', 'ccvm', 'value')),
+    (424, 'item_lang', 'tem', oils_i18n_gettext('424', 'Temne', 'ccvm', 'value')),
+    (425, 'item_lang', 'ter', oils_i18n_gettext('425', 'Terena', 'ccvm', 'value')),
+    (426, 'item_lang', 'tet', oils_i18n_gettext('426', 'Tetum', 'ccvm', 'value')),
+    (427, 'item_lang', 'tgk', oils_i18n_gettext('427', 'Tajik', 'ccvm', 'value')),
+    (428, 'item_lang', 'tgl', oils_i18n_gettext('428', 'Tagalog', 'ccvm', 'value')),
+    (429, 'item_lang', 'tha', oils_i18n_gettext('429', 'Thai', 'ccvm', 'value')),
+    (430, 'item_lang', 'tib', oils_i18n_gettext('430', 'Tibetan', 'ccvm', 'value')),
+    (431, 'item_lang', 'tig', oils_i18n_gettext('431', 'Tigré', 'ccvm', 'value')),
+    (432, 'item_lang', 'tir', oils_i18n_gettext('432', 'Tigrinya', 'ccvm', 'value')),
+    (433, 'item_lang', 'tiv', oils_i18n_gettext('433', 'Tiv', 'ccvm', 'value')),
+    (434, 'item_lang', 'tkl', oils_i18n_gettext('434', 'Tokelauan', 'ccvm', 'value')),
+    (435, 'item_lang', 'tli', oils_i18n_gettext('435', 'Tlingit', 'ccvm', 'value')),
+    (436, 'item_lang', 'tmh', oils_i18n_gettext('436', 'Tamashek', 'ccvm', 'value')),
+    (437, 'item_lang', 'tog', oils_i18n_gettext('437', 'Tonga (Nyasa)', 'ccvm', 'value')),
+    (438, 'item_lang', 'ton', oils_i18n_gettext('438', 'Tongan', 'ccvm', 'value')),
+    (439, 'item_lang', 'tpi', oils_i18n_gettext('439', 'Tok Pisin', 'ccvm', 'value')),
+    (440, 'item_lang', '-tru', oils_i18n_gettext('440', 'Truk', 'ccvm', 'value')),
+    (441, 'item_lang', 'tsi', oils_i18n_gettext('441', 'Tsimshian', 'ccvm', 'value')),
+    (442, 'item_lang', 'tsn', oils_i18n_gettext('442', 'Tswana', 'ccvm', 'value')),
+    (443, 'item_lang', 'tso', oils_i18n_gettext('443', 'Tsonga', 'ccvm', 'value')),
+    (444, 'item_lang', '-tsw', oils_i18n_gettext('444', 'Tswana', 'ccvm', 'value')),
+    (445, 'item_lang', 'tuk', oils_i18n_gettext('445', 'Turkmen', 'ccvm', 'value')),
+    (446, 'item_lang', 'tum', oils_i18n_gettext('446', 'Tumbuka', 'ccvm', 'value')),
+    (447, 'item_lang', 'tup', oils_i18n_gettext('447', 'Tupi languages', 'ccvm', 'value')),
+    (448, 'item_lang', 'tur', oils_i18n_gettext('448', 'Turkish', 'ccvm', 'value')),
+    (449, 'item_lang', 'tut', oils_i18n_gettext('449', 'Altaic (Other)', 'ccvm', 'value')),
+    (450, 'item_lang', 'tvl', oils_i18n_gettext('450', 'Tuvaluan', 'ccvm', 'value')),
+    (451, 'item_lang', 'twi', oils_i18n_gettext('451', 'Twi', 'ccvm', 'value')),
+    (452, 'item_lang', 'tyv', oils_i18n_gettext('452', 'Tuvinian', 'ccvm', 'value')),
+    (453, 'item_lang', 'udm', oils_i18n_gettext('453', 'Udmurt', 'ccvm', 'value')),
+    (454, 'item_lang', 'uga', oils_i18n_gettext('454', 'Ugaritic', 'ccvm', 'value')),
+    (455, 'item_lang', 'uig', oils_i18n_gettext('455', 'Uighur', 'ccvm', 'value')),
+    (456, 'item_lang', 'ukr', oils_i18n_gettext('456', 'Ukrainian', 'ccvm', 'value')),
+    (457, 'item_lang', 'umb', oils_i18n_gettext('457', 'Umbundu', 'ccvm', 'value')),
+    (458, 'item_lang', 'und', oils_i18n_gettext('458', 'Undetermined', 'ccvm', 'value')),
+    (459, 'item_lang', 'urd', oils_i18n_gettext('459', 'Urdu', 'ccvm', 'value')),
+    (460, 'item_lang', 'uzb', oils_i18n_gettext('460', 'Uzbek', 'ccvm', 'value')),
+    (461, 'item_lang', 'vai', oils_i18n_gettext('461', 'Vai', 'ccvm', 'value')),
+    (462, 'item_lang', 'ven', oils_i18n_gettext('462', 'Venda', 'ccvm', 'value')),
+    (463, 'item_lang', 'vie', oils_i18n_gettext('463', 'Vietnamese', 'ccvm', 'value')),
+    (464, 'item_lang', 'vol', oils_i18n_gettext('464', 'Volapük', 'ccvm', 'value')),
+    (465, 'item_lang', 'vot', oils_i18n_gettext('465', 'Votic', 'ccvm', 'value')),
+    (466, 'item_lang', 'wak', oils_i18n_gettext('466', 'Wakashan languages', 'ccvm', 'value')),
+    (467, 'item_lang', 'wal', oils_i18n_gettext('467', 'Walamo', 'ccvm', 'value')),
+    (468, 'item_lang', 'war', oils_i18n_gettext('468', 'Waray', 'ccvm', 'value')),
+    (469, 'item_lang', 'was', oils_i18n_gettext('469', 'Washo', 'ccvm', 'value')),
+    (470, 'item_lang', 'wel', oils_i18n_gettext('470', 'Welsh', 'ccvm', 'value')),
+    (471, 'item_lang', 'wen', oils_i18n_gettext('471', 'Sorbian languages', 'ccvm', 'value')),
+    (472, 'item_lang', 'wln', oils_i18n_gettext('472', 'Walloon', 'ccvm', 'value')),
+    (473, 'item_lang', 'wol', oils_i18n_gettext('473', 'Wolof', 'ccvm', 'value')),
+    (474, 'item_lang', 'xal', oils_i18n_gettext('474', 'Kalmyk', 'ccvm', 'value')),
+    (475, 'item_lang', 'xho', oils_i18n_gettext('475', 'Xhosa', 'ccvm', 'value')),
+    (476, 'item_lang', 'yao', oils_i18n_gettext('476', 'Yao (Africa)', 'ccvm', 'value')),
+    (477, 'item_lang', 'yap', oils_i18n_gettext('477', 'Yapese', 'ccvm', 'value')),
+    (478, 'item_lang', 'yid', oils_i18n_gettext('478', 'Yiddish', 'ccvm', 'value')),
+    (479, 'item_lang', 'yor', oils_i18n_gettext('479', 'Yoruba', 'ccvm', 'value')),
+    (480, 'item_lang', 'ypk', oils_i18n_gettext('480', 'Yupik languages', 'ccvm', 'value')),
+    (481, 'item_lang', 'zap', oils_i18n_gettext('481', 'Zapotec', 'ccvm', 'value')),
+    (482, 'item_lang', 'zen', oils_i18n_gettext('482', 'Zenaga', 'ccvm', 'value')),
+    (483, 'item_lang', 'zha', oils_i18n_gettext('483', 'Zhuang', 'ccvm', 'value')),
+    (484, 'item_lang', 'znd', oils_i18n_gettext('484', 'Zande', 'ccvm', 'value')),
+    (485, 'item_lang', 'zul', oils_i18n_gettext('485', 'Zulu', 'ccvm', 'value')),
+    (486, 'item_lang', 'zun', oils_i18n_gettext('486', 'Zuni', 'ccvm', 'value'));
+
+INSERT INTO config.coded_value_map (id, ctype, code, value, description) VALUES 
+    (487,'audience', ' ', oils_i18n_gettext('487', 'Unknown or unspecified', 'ccvm', 'value'),  oils_i18n_gettext('487', 'The target audience for the item not known or not specified.', 'ccvm', 'description')),
+    (488,'audience', 'a', oils_i18n_gettext('488', 'Preschool', 'ccvm', 'value'),               oils_i18n_gettext('488', 'The item is intended for children, approximate ages 0-5 years.', 'ccvm', 'description')),
+    (489,'audience', 'b', oils_i18n_gettext('489', 'Primary', 'ccvm', 'value'),                 oils_i18n_gettext('489', 'The item is intended for children, approximate ages 6-8 years.', 'ccvm', 'description')),
+    (490,'audience', 'c', oils_i18n_gettext('490', 'Pre-adolescent', 'ccvm', 'value'),          oils_i18n_gettext('490', 'The item is intended for young people, approximate ages 9-13 years.', 'ccvm', 'description')),
+    (491,'audience', 'd', oils_i18n_gettext('491', 'Adolescent', 'ccvm', 'value'),              oils_i18n_gettext('491', 'The item is intended for young people, approximate ages 14-17 years.', 'ccvm', 'description')),
+    (492,'audience', 'e', oils_i18n_gettext('492', 'Adult', 'ccvm', 'value'),                   oils_i18n_gettext('492', 'The item is intended for adults.', 'ccvm', 'description')),
+    (493,'audience', 'f', oils_i18n_gettext('493', 'Specialized', 'ccvm', 'value'),             oils_i18n_gettext('493', 'The item is aimed at a particular audience and the nature of the presentation makes the item of little interest to another audience.', 'ccvm', 'description')),
+    (494,'audience', 'g', oils_i18n_gettext('494', 'General', 'ccvm', 'value'),                 oils_i18n_gettext('494', 'The item is of general interest and not aimed at an audience of a particular intellectual level.', 'ccvm', 'description')),
+    (495,'audience', 'j', oils_i18n_gettext('495', 'Juvenile', 'ccvm', 'value'),                oils_i18n_gettext('495', 'The item is intended for children and young people, approximate ages 0-15 years.', 'ccvm', 'description'));
+
+INSERT INTO config.coded_value_map (id, ctype, code, value) VALUES
+    (496, 'item_type', 'a', oils_i18n_gettext('496', 'Language material', 'ccvm', 'value')),
+    (497, 'item_type', 't', oils_i18n_gettext('497', 'Manuscript language material', 'ccvm', 'value')),
+    (498, 'item_type', 'g', oils_i18n_gettext('498', 'Projected medium', 'ccvm', 'value')),
+    (499, 'item_type', 'k', oils_i18n_gettext('499', 'Two-dimensional nonprojectable graphic', 'ccvm', 'value')),
+    (500, 'item_type', 'r', oils_i18n_gettext('500', 'Three-dimensional artifact or naturally occurring object', 'ccvm', 'value')),
+    (501, 'item_type', 'o', oils_i18n_gettext('501', 'Kit', 'ccvm', 'value')),
+    (502, 'item_type', 'p', oils_i18n_gettext('502', 'Mixed materials', 'ccvm', 'value')),
+    (503, 'item_type', 'e', oils_i18n_gettext('503', 'Cartographic material', 'ccvm', 'value')),
+    (504, 'item_type', 'f', oils_i18n_gettext('504', 'Manuscript cartographic material', 'ccvm', 'value')),
+    (505, 'item_type', 'c', oils_i18n_gettext('505', 'Notated music', 'ccvm', 'value')),
+    (506, 'item_type', 'd', oils_i18n_gettext('506', 'Manuscript notated music', 'ccvm', 'value')),
+    (507, 'item_type', 'i', oils_i18n_gettext('507', 'Nonmusical sound recording', 'ccvm', 'value')),
+    (508, 'item_type', 'j', oils_i18n_gettext('508', 'Musical sound recording', 'ccvm', 'value')),
+    (509, 'item_type', 'm', oils_i18n_gettext('509', 'Computer file', 'ccvm', 'value'));
+
+INSERT INTO config.coded_value_map (id, ctype, code, value, description) VALUES 
+    (510, 'lit_form', '0', oils_i18n_gettext('510', 'Not fiction (not further specified)', 'ccvm', 'value'), oils_i18n_gettext('510', 'The item is not a work of fiction and no further identification of the literary form is desired', 'ccvm', 'description')),
+    (511, 'lit_form', '1', oils_i18n_gettext('511', 'Fiction (not further specified)', 'ccvm', 'value'),     oils_i18n_gettext('511', 'The item is a work of fiction and no further identification of the literary form is desired', 'ccvm', 'description')),
+    (512, 'lit_form', 'c', oils_i18n_gettext('512', 'Comic strips', 'ccvm', 'value'), NULL),
+    (513, 'lit_form', 'd', oils_i18n_gettext('513', 'Dramas', 'ccvm', 'value'), NULL),
+    (514, 'lit_form', 'e', oils_i18n_gettext('514', 'Essays', 'ccvm', 'value'), NULL),
+    (515, 'lit_form', 'f', oils_i18n_gettext('515', 'Novels', 'ccvm', 'value'), NULL),
+    (516, 'lit_form', 'h', oils_i18n_gettext('516', 'Humor, satires, etc.', 'ccvm', 'value'),                oils_i18n_gettext('516', 'The item is a humorous work, satire or of similar literary form.', 'ccvm', 'description')),
+    (517, 'lit_form', 'i', oils_i18n_gettext('517', 'Letters', 'ccvm', 'value'),                             oils_i18n_gettext('517', 'The item is a single letter or collection of correspondence.', 'ccvm', 'description')),
+    (518, 'lit_form', 'j', oils_i18n_gettext('518', 'Short stories', 'ccvm', 'value'),                       oils_i18n_gettext('518', 'The item is a short story or collection of short stories.', 'ccvm', 'description')),
+    (519, 'lit_form', 'm', oils_i18n_gettext('519', 'Mixed forms', 'ccvm', 'value'),                         oils_i18n_gettext('519', 'The item is a variety of literary forms (e.g., poetry and short stories).', 'ccvm', 'description')),
+    (520, 'lit_form', 'p', oils_i18n_gettext('520', 'Poetry', 'ccvm', 'value'),                              oils_i18n_gettext('520', 'The item is a poem or collection of poems.', 'ccvm', 'description')),
+    (521, 'lit_form', 's', oils_i18n_gettext('521', 'Speeches', 'ccvm', 'value'),                            oils_i18n_gettext('521', 'The item is a speech or collection of speeches.', 'ccvm', 'description')),
+    (522, 'lit_form', 'u', oils_i18n_gettext('522', 'Unknown', 'ccvm', 'value'),                             oils_i18n_gettext('522', 'The literary form of the item is unknown.', 'ccvm', 'description'));
+
+
+INSERT INTO config.coded_value_map (id, ctype, code, value) VALUES
+    (523, 'item_form', 'a', oils_i18n_gettext('523', 'Microfilm', 'ccvm', 'value')),
+    (524, 'item_form', 'b', oils_i18n_gettext('524', 'Microfiche', 'ccvm', 'value')),
+    (525, 'item_form', 'c', oils_i18n_gettext('525', 'Microopaque', 'ccvm', 'value')),
+    (526, 'item_form', 'd', oils_i18n_gettext('526', 'Large print', 'ccvm', 'value')),
+    (527, 'item_form', 'f', oils_i18n_gettext('527', 'Braille', 'ccvm', 'value')),
+    (528, 'item_form', 'r', oils_i18n_gettext('528', 'Regular print reproduction', 'ccvm', 'value')),
+    (529, 'item_form', 's', oils_i18n_gettext('529', 'Electronic', 'ccvm', 'value'));
+
+INSERT INTO config.coded_value_map (id, ctype, code, value) VALUES
+    (530, 'bib_level', 'a', oils_i18n_gettext('530', 'Monographic component part', 'ccvm', 'value')),
+    (531, 'bib_level', 'b', oils_i18n_gettext('531', 'Serial component part', 'ccvm', 'value')),
+    (532, 'bib_level', 'c', oils_i18n_gettext('532', 'Collection', 'ccvm', 'value')),
+    (533, 'bib_level', 'd', oils_i18n_gettext('533', 'Subunit', 'ccvm', 'value')),
+    (534, 'bib_level', 'i', oils_i18n_gettext('534', 'Integrating resource', 'ccvm', 'value')),
+    (535, 'bib_level', 'm', oils_i18n_gettext('535', 'Monograph/Item', 'ccvm', 'value')),
+    (536, 'bib_level', 's', oils_i18n_gettext('536', 'Serial', 'ccvm', 'value'));
+
+
 -- Trigger Event Definitions -------------------------------------------------
 
 -- Sample Overdue Notice --
@@ -4953,6 +5172,34 @@ INSERT INTO config.metabib_field_index_norm_map (field,norm,params)
       WHERE i.func IN ('replace')
             AND m.id IN (19);
 
+INSERT INTO config.record_attr_index_norm_map (attr,norm,pos)
+    SELECT  m.name, i.id, 0
+      FROM  config.record_attr_definition m,
+            config.index_normalizer i
+      WHERE i.func IN ('content_or_null')
+            AND m.name IN ('titlesort', 'authorsort');
+
+INSERT INTO config.record_attr_index_norm_map (attr,norm,pos)
+    SELECT  m.name, i.id, 0
+      FROM  config.record_attr_definition m,
+            config.index_normalizer i
+      WHERE i.func IN ('integer_or_null')
+            AND m.name IN ('date1', 'date2', 'pubdate');
+
+INSERT INTO config.record_attr_index_norm_map (attr,norm,pos)
+    SELECT  m.name, i.id, 0
+      FROM  config.record_attr_definition m,
+            config.index_normalizer i
+      WHERE i.func IN ('approximate_low_date')
+            AND m.name IN ('date1', 'pubdate');
+
+INSERT INTO config.record_attr_index_norm_map (attr,norm,pos)
+    SELECT  m.name, i.id, 0
+      FROM  config.record_attr_definition m,
+            config.index_normalizer i
+      WHERE i.func IN ('approximate_high_date')
+            AND m.name IN ('date2');
+
 -- claims never checked out mark item missing 
 INSERT INTO
     config.org_unit_setting_type ( name, label, description, datatype )
diff --git a/Open-ILS/src/sql/Pg/upgrade/0494.data.search_indexes.sql b/Open-ILS/src/sql/Pg/upgrade/0494.data.search_indexes.sql
new file mode 100644 (file)
index 0000000..79ebb44
--- /dev/null
@@ -0,0 +1,13 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0494'); -- dbs
+
+UPDATE config.metabib_field
+    SET xpath = $$//mods32:mods/mods32:subject$$
+    WHERE field_class = 'subject' AND name = 'complete';
+
+UPDATE config.metabib_field
+    SET xpath = $$//marc:datafield[@tag='099']$$
+    WHERE field_class = 'identifier' AND name = 'bibcn';
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0495.schema.svf-phase-one.sql b/Open-ILS/src/sql/Pg/upgrade/0495.schema.svf-phase-one.sql
new file mode 100644 (file)
index 0000000..281dfe1
--- /dev/null
@@ -0,0 +1,519 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0495'); -- miker
+
+CREATE TABLE config.record_attr_definition (
+    name        TEXT    PRIMARY KEY,
+    label       TEXT    NOT NULL, -- I18N
+    description TEXT,
+    filter      BOOL    NOT NULL DEFAULT TRUE,  -- becomes QP filter if true
+    sorter      BOOL    NOT NULL DEFAULT FALSE, -- becomes QP sort() axis if true
+
+-- For pre-extracted fields. Takes the first occurance, uses naive subfield ordering
+    tag         TEXT, -- LIKE format
+    sf_list     TEXT, -- pile-o-values, like 'abcd' for a and b and c and d
+
+-- This is used for both tag/sf and xpath entries
+    joiner      TEXT,
+
+-- For xpath-extracted attrs
+    xpath       TEXT,
+    format      TEXT    REFERENCES config.xml_transform (name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    start_pos   INT,
+    string_len  INT,
+
+-- For fixed fields
+    fixed_field TEXT, -- should exist in config.marc21_ff_pos_map.fixed_field
+
+-- For phys-char fields
+    phys_char_sf    INT REFERENCES config.marc21_physical_characteristic_subfield_map (id)
+);
+
+CREATE TABLE config.record_attr_index_norm_map (
+    id      SERIAL  PRIMARY KEY,
+    attr    TEXT    NOT NULL REFERENCES config.record_attr_definition (name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    norm    INT     NOT NULL REFERENCES config.index_normalizer (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    params  TEXT,
+    pos     INT     NOT NULL DEFAULT 0
+);
+
+CREATE TABLE config.coded_value_map (
+    id          SERIAL  PRIMARY KEY,
+    ctype       TEXT    NOT NULL REFERENCES config.record_attr_definition (name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+    code        TEXT    NOT NULL,
+    value       TEXT    NOT NULL,
+    description TEXT
+);
+
+-- record attributes
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('alph','Alph','Alph');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('audience','Audn','Audn');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('bib_level','BLvl','BLvl');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('biog','Biog','Biog');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('conf','Conf','Conf');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('control_type','Ctrl','Ctrl');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('ctry','Ctry','Ctry');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('date1','Date1','Date1');
+INSERT INTO config.record_attr_definition (name,label,fixed_field,sorter,filter) values ('pubdate','Pub Date','Date1',TRUE,FALSE);
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('date2','Date2','Date2');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('cat_form','Desc','Desc');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('pub_status','DtSt','DtSt');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('enc_level','ELvl','ELvl');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('fest','Fest','Fest');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('item_form','Form','Form');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('gpub','GPub','GPub');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('ills','Ills','Ills');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('indx','Indx','Indx');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('item_lang','Lang','Lang');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('lit_form','LitF','LitF');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('mrec','MRec','MRec');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('ff_sl','S/L','S/L');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('type_mat','TMat','TMat');
+INSERT INTO config.record_attr_definition (name,label,fixed_field) values ('item_type','Type','Type');
+INSERT INTO config.record_attr_definition (name,label,phys_char_sf) values ('vr_format','Videorecording format',72);
+INSERT INTO config.record_attr_definition (name,label,sorter,filter,tag) values ('titlesort','Title',TRUE,FALSE,'tnf');
+INSERT INTO config.record_attr_definition (name,label,sorter,filter,tag) values ('authorsort','Author',TRUE,FALSE,'1%');
+
+INSERT INTO config.coded_value_map (ctype,code,value,description)
+    SELECT 'item_lang' AS ctype, code, value, NULL FROM config.language_map
+        UNION
+    SELECT 'bib_level' AS ctype, code, value, NULL FROM config.bib_level_map
+        UNION
+    SELECT 'item_form' AS ctype, code, value, NULL FROM config.item_form_map
+        UNION
+    SELECT 'item_type' AS ctype, code, value, NULL FROM config.item_type_map
+        UNION
+    SELECT 'lit_form' AS ctype, code, value, description FROM config.lit_form_map
+        UNION
+    SELECT 'audience' AS ctype, code, value, description FROM config.audience_map
+        UNION
+    SELECT 'vr_format' AS ctype, code, value, NULL FROM config.videorecording_format_map;
+
+ALTER TABLE config.i18n_locale DROP CONSTRAINT i18n_locale_marc_code_fkey;
+
+ALTER TABLE config.circ_matrix_matchpoint DROP CONSTRAINT circ_matrix_matchpoint_marc_form_fkey;
+ALTER TABLE config.circ_matrix_matchpoint DROP CONSTRAINT circ_matrix_matchpoint_marc_type_fkey;
+ALTER TABLE config.circ_matrix_matchpoint DROP CONSTRAINT circ_matrix_matchpoint_marc_vr_format_fkey;
+
+ALTER TABLE config.hold_matrix_matchpoint DROP CONSTRAINT hold_matrix_matchpoint_marc_form_fkey;
+ALTER TABLE config.hold_matrix_matchpoint DROP CONSTRAINT hold_matrix_matchpoint_marc_type_fkey;
+ALTER TABLE config.hold_matrix_matchpoint DROP CONSTRAINT hold_matrix_matchpoint_marc_vr_format_fkey;
+
+DROP TABLE config.language_map;
+DROP TABLE config.bib_level_map;
+DROP TABLE config.item_form_map;
+DROP TABLE config.item_type_map;
+DROP TABLE config.lit_form_map;
+DROP TABLE config.audience_map;
+DROP TABLE config.videorecording_format_map;
+
+UPDATE config.i18n_core SET fq_field = 'ccvm.value', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'clm.value' AND ccvm.ctype = 'item_lang' AND identity_value = ccvm.code;
+UPDATE config.i18n_core SET fq_field = 'ccvm.value', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'cblvl.value' AND ccvm.ctype = 'bib_level' AND identity_value = ccvm.code;
+UPDATE config.i18n_core SET fq_field = 'ccvm.value', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'cifm.value' AND ccvm.ctype = 'item_form' AND identity_value = ccvm.code;
+UPDATE config.i18n_core SET fq_field = 'ccvm.value', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'citm.value' AND ccvm.ctype = 'item_type' AND identity_value = ccvm.code;
+UPDATE config.i18n_core SET fq_field = 'ccvm.value', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'clfm.value' AND ccvm.ctype = 'lit_form' AND identity_value = ccvm.code;
+UPDATE config.i18n_core SET fq_field = 'ccvm.value', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'cam.value' AND ccvm.ctype = 'audience' AND identity_value = ccvm.code;
+UPDATE config.i18n_core SET fq_field = 'ccvm.value', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'cvrfm.value' AND ccvm.ctype = 'vr_format' AND identity_value = ccvm.code;
+
+UPDATE config.i18n_core SET fq_field = 'ccvm.description', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'clfm.description' AND ccvm.ctype = 'lit_form' AND identity_value = ccvm.code;
+UPDATE config.i18n_core SET fq_field = 'ccvm.description', identity_value = ccvm.id FROM config.coded_value_map AS ccvm WHERE fq_field = 'cam.description' AND ccvm.ctype = 'audience' AND identity_value = ccvm.code;
+
+CREATE VIEW config.language_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'item_lang';
+CREATE VIEW config.bib_level_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'bib_level';
+CREATE VIEW config.item_form_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'item_form';
+CREATE VIEW config.item_type_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'item_type';
+CREATE VIEW config.lit_form_map AS SELECT code, value, description FROM config.coded_value_map WHERE ctype = 'lit_form';
+CREATE VIEW config.audience_map AS SELECT code, value, description FROM config.coded_value_map WHERE ctype = 'audience';
+CREATE VIEW config.videorecording_format_map AS SELECT code, value FROM config.coded_value_map WHERE ctype = 'vr_format';
+
+CREATE TABLE metabib.record_attr (
+       id              BIGINT  PRIMARY KEY REFERENCES biblio.record_entry (id) ON DELETE CASCADE,
+       attrs   HSTORE  NOT NULL DEFAULT ''::HSTORE
+);
+CREATE INDEX metabib_svf_attrs_idx ON metabib.record_attr USING GIST (attrs);
+CREATE INDEX metabib_svf_date1_idx ON metabib.record_attr ( (attrs->'date1') );
+CREATE INDEX metabib_svf_dates_idx ON metabib.record_attr ( (attrs->'date1'), (attrs->'date2') );
+
+INSERT INTO metabib.record_attr (id,attrs)
+    SELECT mrd.record, hstore(mrd) - '{id,record}'::TEXT[] FROM metabib.rec_descriptor mrd;
+
+-- Back-compat view ... we're moving to an HSTORE world
+CREATE TYPE metabib.rec_desc_type AS (
+    item_type       TEXT,
+    item_form       TEXT,
+    bib_level       TEXT,
+    control_type    TEXT,
+    char_encoding   TEXT,
+    enc_level       TEXT,
+    audience        TEXT,
+    lit_form        TEXT,
+    type_mat        TEXT,
+    cat_form        TEXT,
+    pub_status      TEXT,
+    item_lang       TEXT,
+    vr_format       TEXT,
+    date1           TEXT,
+    date2           TEXT
+);
+
+DROP TABLE metabib.rec_descriptor CASCADE;
+
+CREATE VIEW metabib.rec_descriptor AS
+    SELECT  id,
+            id AS record,
+            (populate_record(NULL::metabib.rec_desc_type, attrs)).*
+      FROM  metabib.record_attr;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
+DECLARE
+    ldr         TEXT;
+    tval        TEXT;
+    tval_rec    RECORD;
+    bval        TEXT;
+    bval_rec    RECORD;
+    retval      config.marc21_rec_type_map%ROWTYPE;
+BEGIN
+    ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
+
+    IF ldr IS NULL OR ldr = '' THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+        RETURN retval;
+    END IF;
+
+    SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
+    SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
+
+
+    tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
+    bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
+
+    -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
+
+    SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
+
+
+    IF retval.code IS NULL THEN
+        SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
+    END IF;
+
+    RETURN retval;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_record_type( rid BIGINT ) RETURNS config.marc21_rec_type_map AS $func$
+    SELECT * FROM vandelay.marc21_record_type( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
+DECLARE
+    rtype       TEXT;
+    ff_pos      RECORD;
+    tag_data    RECORD;
+    val         TEXT;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
+        FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
+            val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+            RETURN val;
+        END LOOP;
+        val := REPEAT( ff_pos.default_val, ff_pos.length );
+        RETURN val;
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_fixed_field( rid BIGINT, ff TEXT ) RETURNS TEXT AS $func$
+    SELECT * FROM vandelay.marc21_extract_fixed_field( (SELECT marc FROM biblio.record_entry WHERE id = $1), $2 );
+$func$ LANGUAGE SQL;
+
+CREATE TYPE biblio.record_ff_map AS (record BIGINT, ff_name TEXT, ff_value TEXT);
+CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
+DECLARE
+    tag_data    TEXT;
+    rtype       TEXT;
+    ff_pos      RECORD;
+    output      biblio.record_ff_map%ROWTYPE;
+BEGIN
+    rtype := (vandelay.marc21_record_type( marc )).code;
+
+    FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
+        output.ff_name  := ff_pos.fixed_field;
+        output.ff_value := NULL;
+
+        FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(tag) || '"]/text()', marc ) ) x(value) LOOP
+            output.ff_value := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
+            IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
+            RETURN NEXT output;
+            output.ff_value := NULL;
+        END LOOP;
+
+    END LOOP;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_extract_all_fixed_fields( rid BIGINT ) RETURNS SETOF biblio.record_ff_map AS $func$
+    SELECT $1 AS record, ff_name, ff_value FROM vandelay.marc21_extract_all_fixed_fields( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+DECLARE
+    rowid   INT := 0;
+    _007    TEXT;
+    ptype   config.marc21_physical_characteristic_type_map%ROWTYPE;
+    psf     config.marc21_physical_characteristic_subfield_map%ROWTYPE;
+    pval    config.marc21_physical_characteristic_value_map%ROWTYPE;
+    retval  biblio.marc21_physical_characteristics%ROWTYPE;
+BEGIN
+
+    _007 := oils_xpath_string( '//*[@tag="007"]', marc );
+
+    IF _007 IS NOT NULL AND _007 <> '' THEN
+        SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
+
+        IF ptype.ptype_key IS NOT NULL THEN
+            FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
+                SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
+
+                IF pval.id IS NOT NULL THEN
+                    rowid := rowid + 1;
+                    retval.id := rowid;
+                    retval.ptype := ptype.ptype_key;
+                    retval.subfield := psf.id;
+                    retval.value := pval.id;
+                    RETURN NEXT retval;
+                END IF;
+
+            END LOOP;
+        END IF;
+    END IF;
+
+    RETURN;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION biblio.marc21_physical_characteristics( rid BIGINT ) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
+    SELECT id, $1 AS record, ptype, subfield, value FROM vandelay.marc21_physical_characteristics( (SELECT marc FROM biblio.record_entry WHERE id = $1) );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION biblio.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
+DECLARE
+    transformed_xml TEXT;
+    prev_xfrm       TEXT;
+    normalizer      RECORD;
+    xfrm            config.xml_transform%ROWTYPE;
+    attr_value      TEXT;
+    new_attrs       HSTORE := ''::HSTORE;
+    attr_def        config.record_attr_definition%ROWTYPE;
+BEGIN
+
+    IF NEW.deleted IS TRUE THEN -- If this bib is deleted
+        DELETE FROM metabib.metarecord_source_map WHERE source = NEW.id; -- Rid ourselves of the search-estimate-killing linkage
+        DELETE FROM metabib.record_attr WHERE id = NEW.id; -- Kill the attrs hash, useless on deleted records
+        DELETE FROM authority.bib_linking WHERE bib = NEW.id; -- Avoid updating fields in bibs that are no longer visible
+        RETURN NEW; -- and we're done
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN -- re-ingest?
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
+
+        IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
+            RETURN NEW;
+        END IF;
+    END IF;
+
+    -- Record authority linking
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_linking' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM biblio.map_authority_linking( NEW.id, NEW.marc );
+    END IF;
+
+    -- Flatten and insert the mfr data
+    PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_full_rec' AND enabled;
+    IF NOT FOUND THEN
+        PERFORM metabib.reingest_metabib_full_rec(NEW.id);
+
+        -- Now we pull out attribute data, which is dependent on the mfr for all but XPath-based fields
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_metabib_rec_descriptor' AND enabled;
+        IF NOT FOUND THEN
+            FOR attr_def IN SELECT * FROM config.record_attr_definition ORDER BY format LOOP
+
+                IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
+                    SELECT  ARRAY_TO_STRING(ARRAY_ACCUM(value), COALESCE(attr_def.joiner,' ')) INTO attr_value
+                      FROM  (SELECT * FROM metabib.full_rec ORDER BY tag, subfield) AS x
+                      WHERE record = NEW.id
+                            AND tag LIKE attr_def.tag
+                            AND CASE
+                                WHEN attr_def.sf_list IS NOT NULL
+                                    THEN POSITION(subfield IN attr_def.sf_list) > 0
+                                ELSE TRUE
+                                END
+                      GROUP BY tag
+                      ORDER BY tag
+                      LIMIT 1;
+
+                ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
+                    attr_value := biblio.marc21_extract_fixed_field(NEW.id, attr_def.fixed_field);
+
+                ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
+
+                    SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
+
+                    -- See if we can skip the XSLT ... it's expensive
+                    IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
+                        -- Can't skip the transform
+                        IF xfrm.xslt <> '---' THEN
+                            transformed_xml := oils_xslt_process(NEW.marc,xfrm.xslt);
+                        ELSE
+                            transformed_xml := NEW.marc;
+                        END IF;
+
+                        prev_xfrm := xfrm.name;
+                    END IF;
+
+                    IF xfrm.name IS NULL THEN
+                        -- just grab the marcxml (empty) transform
+                        SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
+                        prev_xfrm := xfrm.name;
+                    END IF;
+
+                    attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
+
+                ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
+                    SELECT  value::TEXT INTO attr_value
+                      FROM  biblio.marc21_physical_characteristics(NEW.id)
+                      WHERE subfield = attr_def.phys_char_sf
+                      LIMIT 1; -- Just in case ...
+
+                END IF;
+
+                -- apply index normalizers to attr_value
+                FOR normalizer IN
+                    SELECT  n.func AS func,
+                            n.param_count AS param_count,
+                            m.params AS params
+                      FROM  config.index_normalizer n
+                            JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
+                      WHERE attr = attr_def.name
+                      ORDER BY m.pos LOOP
+                        EXECUTE 'SELECT ' || normalizer.func || '(' ||
+                            quote_literal( attr_value ) ||
+                            CASE
+                                WHEN normalizer.param_count > 0
+                                    THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
+                                    ELSE ''
+                                END ||
+                            ')' INTO attr_value;
+
+                END LOOP;
+
+                -- Add the new value to the hstore
+                new_attrs := new_attrs || hstore( attr_def.name, attr_value );
+
+            END LOOP;
+
+            IF TG_OP = 'INSERT' OR OLD.deleted THEN -- initial insert OR revivication
+                INSERT INTO metabib.record_attr (id, attrs) VALUES (NEW.id, new_attrs);
+            ELSE
+                UPDATE metabib.record_attr SET attrs = attrs || new_attrs WHERE id = NEW.id;
+            END IF;
+
+        END IF;
+    END IF;
+
+    -- Gather and insert the field entry data
+    PERFORM metabib.reingest_metabib_field_entries(NEW.id);
+
+    -- Located URI magic
+    IF TG_OP = 'INSERT' THEN
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    ELSE
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_located_uri' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM biblio.extract_located_uris( NEW.id, NEW.marc, NEW.editor );
+        END IF;
+    END IF;
+
+    -- (re)map metarecord-bib linking
+    IF TG_OP = 'INSERT' THEN -- if not deleted and performing an insert, check for the flag
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_insert' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
+        END IF;
+    ELSE -- we're doing an update, and we're not deleted, remap
+        PERFORM * FROM config.internal_flag WHERE name = 'ingest.metarecord_mapping.skip_on_update' AND enabled;
+        IF NOT FOUND THEN
+            PERFORM metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
+        END IF;
+    END IF;
+
+    RETURN NEW;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+DROP FUNCTION metabib.reingest_metabib_rec_descriptor( bib_id BIGINT );
+
+CREATE OR REPLACE FUNCTION public.approximate_date( TEXT, TEXT ) RETURNS TEXT AS $func$
+        SELECT REGEXP_REPLACE( $1, E'\\D', $2, 'g' );
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.approximate_low_date( TEXT ) RETURNS TEXT AS $func$
+        SELECT approximate_date( $1, '0');
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.approximate_high_date( TEXT ) RETURNS TEXT AS $func$
+        SELECT approximate_date( $1, '9');
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.integer_or_null( TEXT ) RETURNS TEXT AS $func$
+        SELECT CASE WHEN $1 ~ E'^\\d+$' THEN $1 ELSE NULL END
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.content_or_null( TEXT ) RETURNS TEXT AS $func$
+        SELECT CASE WHEN $1 ~ E'^\\s*$' THEN NULL ELSE $1 END
+$func$ LANGUAGE SQL STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION public.force_to_isbn13( TEXT ) RETURNS TEXT AS $func$
+    use Business::ISBN;
+    use strict;
+    use warnings;
+
+    # Find the first ISBN, force it to ISBN13 and return it
+
+    my $input = shift;
+
+    foreach my $word (split(/\s/, $input)) {
+        my $isbn = Business::ISBN->new($word);
+
+        # First check the checksum; if it is not valid, fix it and add the original
+        # bad-checksum ISBN to the output
+        if ($isbn && $isbn->is_valid_checksum() == Business::ISBN::BAD_CHECKSUM) {
+            $isbn->fix_checksum();
+        }
+
+        # If we now have a valid ISBN, force it to ISBN13 and return it
+        return $isbn->as_isbn13->isbn if ($isbn && $isbn->is_valid());
+    }
+    return undef;
+$func$ LANGUAGE PLPERLU;
+
+COMMENT ON FUNCTION public.force_to_isbn13(TEXT) IS $$
+/*
+ * Copyright (C) 2011 Equinox Software
+ * Mike Rylander <mrylander@gmail.com>
+ *
+ * Inspired by translate_isbn1013
+ *
+ * The force_to_isbn13 function takes an input ISBN and returns the ISBN13
+ * version without hypens and with a repaired checksum if the checksum was bad
+ */
+$$;
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0496.data.search_indexes.sql b/Open-ILS/src/sql/Pg/upgrade/0496.data.search_indexes.sql
new file mode 100644 (file)
index 0000000..41b4c2d
--- /dev/null
@@ -0,0 +1,25 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0496'); -- dbs
+
+UPDATE config.metabib_field
+    SET xpath = $$//marc:datafield[@tag='024' and @ind1='1']/marc:subfield[@code='a' or @code='z']$$
+    WHERE field_class = 'identifier' AND name = 'upc';
+
+UPDATE config.metabib_field
+    SET xpath = $$//marc:datafield[@tag='024' and @ind1='2']/marc:subfield[@code='a' or @code='z']$$
+    WHERE field_class = 'identifier' AND name = 'ismn';
+
+UPDATE config.metabib_field
+    SET xpath = $$//marc:datafield[@tag='024' and @ind1='3']/marc:subfield[@code='a' or @code='z']$$
+    WHERE field_class = 'identifier' AND name = 'ean';
+
+UPDATE config.metabib_field
+    SET xpath = $$//marc:datafield[@tag='024' and @ind1='0']/marc:subfield[@code='a' or @code='z']$$
+    WHERE field_class = 'identifier' AND name = 'isrc';
+
+UPDATE config.metabib_field
+    SET xpath = $$//marc:datafield[@tag='024' and @ind1='4']/marc:subfield[@code='a' or @code='z']$$
+    WHERE field_class = 'identifier' AND name = 'sici';
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0497.patron_registration.sql b/Open-ILS/src/sql/Pg/upgrade/0497.patron_registration.sql
new file mode 100644 (file)
index 0000000..d5e6182
--- /dev/null
@@ -0,0 +1,225 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0497');
+
+INSERT into config.org_unit_setting_type
+( name, label, description, datatype ) VALUES
+
+( 'ui.patron.edit.au.active.show',
+    oils_i18n_gettext('ui.patron.edit.au.active.show', 'GUI: Show active field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.active.show', 'The active field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.active.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.active.suggest', 'GUI: Suggest active field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.active.suggest', 'The active field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alert_message.show',
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.show', 'GUI: Show alert_message field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.show', 'The alert_message field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alert_message.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.suggest', 'GUI: Suggest alert_message field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alert_message.suggest', 'The alert_message field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alias.show',
+    oils_i18n_gettext('ui.patron.edit.au.alias.show', 'GUI: Show alias field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alias.show', 'The alias field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.alias.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.alias.suggest', 'GUI: Suggest alias field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.alias.suggest', 'The alias field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.barred.show',
+    oils_i18n_gettext('ui.patron.edit.au.barred.show', 'GUI: Show barred field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.barred.show', 'The barred field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.barred.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.barred.suggest', 'GUI: Suggest barred field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.barred.suggest', 'The barred field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_never_checked_out_count.show',
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.show', 'GUI: Show claims_never_checked_out_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.show', 'The claims_never_checked_out_count field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_never_checked_out_count.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.suggest', 'GUI: Suggest claims_never_checked_out_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_never_checked_out_count.suggest', 'The claims_never_checked_out_count field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_returned_count.show',
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.show', 'GUI: Show claims_returned_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.show', 'The claims_returned_count field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.claims_returned_count.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.suggest', 'GUI: Suggest claims_returned_count field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.claims_returned_count.suggest', 'The claims_returned_count field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.day_phone.example',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.example', 'GUI: Example for day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.example', 'The Example for validation on the day_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.day_phone.regex',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.regex', 'GUI: Regex for day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.regex', 'The Regular Expression for validation on the day_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.day_phone.require',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.require', 'GUI: Require day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.require', 'The day_phone field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.day_phone.show',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.show', 'GUI: Show day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.show', 'The day_phone field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.day_phone.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.suggest', 'GUI: Suggest day_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.suggest', 'The day_phone field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.calendar',
+    oils_i18n_gettext('ui.patron.edit.au.dob.calendar', 'GUI: Show calendar widget for dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.calendar', 'If set the calendar widget will appear when editing the dob field on the patron registration form.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.require',
+    oils_i18n_gettext('ui.patron.edit.au.dob.require', 'GUI: Require dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.require', 'The dob field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.show',
+    oils_i18n_gettext('ui.patron.edit.au.dob.show', 'GUI: Show dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.show', 'The dob field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.dob.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.dob.suggest', 'GUI: Suggest dob field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.dob.suggest', 'The dob field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.email.example',
+    oils_i18n_gettext('ui.patron.edit.au.email.example', 'GUI: Example for email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.example', 'The Example for validation on the email field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.email.regex',
+    oils_i18n_gettext('ui.patron.edit.au.email.regex', 'GUI: Regex for email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.regex', 'The Regular Expression for validation on the email field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.email.require',
+    oils_i18n_gettext('ui.patron.edit.au.email.require', 'GUI: Require email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.require', 'The email field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.email.show',
+    oils_i18n_gettext('ui.patron.edit.au.email.show', 'GUI: Show email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.show', 'The email field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.email.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.email.suggest', 'GUI: Suggest email field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.email.suggest', 'The email field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.evening_phone.example',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.example', 'GUI: Example for evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.example', 'The Example for validation on the evening_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.evening_phone.regex',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.regex', 'GUI: Regex for evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.regex', 'The Regular Expression for validation on the evening_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.evening_phone.require',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.require', 'GUI: Require evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.require', 'The evening_phone field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.evening_phone.show',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.show', 'GUI: Show evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.show', 'The evening_phone field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.evening_phone.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.suggest', 'GUI: Suggest evening_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.evening_phone.suggest', 'The evening_phone field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value.show',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.show', 'GUI: Show ident_value field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.show', 'The ident_value field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.suggest', 'GUI: Suggest ident_value field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value.suggest', 'The ident_value field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value2.show',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.show', 'GUI: Show ident_value2 field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.show', 'The ident_value2 field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.ident_value2.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.suggest', 'GUI: Suggest ident_value2 field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.ident_value2.suggest', 'The ident_value2 field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.juvenile.show',
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.show', 'GUI: Show juvenile field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.show', 'The juvenile field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.juvenile.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.suggest', 'GUI: Suggest juvenile field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.juvenile.suggest', 'The juvenile field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.master_account.show',
+    oils_i18n_gettext('ui.patron.edit.au.master_account.show', 'GUI: Show master_account field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.master_account.show', 'The master_account field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.master_account.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.master_account.suggest', 'GUI: Suggest master_account field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.master_account.suggest', 'The master_account field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.other_phone.example',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.example', 'GUI: Example for other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.example', 'The Example for validation on the other_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.other_phone.regex',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.regex', 'GUI: Regex for other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.regex', 'The Regular Expression for validation on the other_phone field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.au.other_phone.require',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.require', 'GUI: Require other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.require', 'The other_phone field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.other_phone.show',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.show', 'GUI: Show other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.show', 'The other_phone field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.other_phone.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.suggest', 'GUI: Suggest other_phone field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.other_phone.suggest', 'The other_phone field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.second_given_name.show',
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.show', 'GUI: Show second_given_name field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.show', 'The second_given_name field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.second_given_name.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.suggest', 'GUI: Suggest second_given_name field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.second_given_name.suggest', 'The second_given_name field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.suffix.show',
+    oils_i18n_gettext('ui.patron.edit.au.suffix.show', 'GUI: Show suffix field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.suffix.show', 'The suffix field will be shown on the patron registration screen. Showing a field makes it appear with required fields even when not required. If the field is required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.au.suffix.suggest',
+    oils_i18n_gettext('ui.patron.edit.au.suffix.suggest', 'GUI: Suggest suffix field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.au.suffix.suggest', 'The suffix field will be suggested on the patron registration screen. Suggesting a field makes it appear when suggested fields are shown. If the field is shown or required this setting is ignored.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.aua.county.require',
+    oils_i18n_gettext('ui.patron.edit.aua.county.require', 'GUI: Require county field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.aua.county.require', 'The county field will be required on the patron registration screen.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.aua.post_code.example',
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.example', 'GUI: Example for post_code field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.example', 'The Example for validation on the post_code field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.aua.post_code.regex',
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.regex', 'GUI: Regex for post_code field on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.aua.post_code.regex', 'The Regular Expression for validation on the post_code field in patron registration.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.default_suggested',
+    oils_i18n_gettext('ui.patron.edit.default_suggested', 'GUI: Default showing suggested patron registration fields', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.default_suggested', 'Instead of All fields, show just suggested fields in patron registration by default.', 'coust', 'description'),
+    'bool'),
+( 'ui.patron.edit.phone.example',
+    oils_i18n_gettext('ui.patron.edit.phone.example', 'GUI: Example for phone fields on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.phone.example', 'The Example for validation on phone fields in patron registration. Applies to all phone fields without their own setting.', 'coust', 'description'),
+    'string'),
+( 'ui.patron.edit.phone.regex',
+    oils_i18n_gettext('ui.patron.edit.phone.regex', 'GUI: Regex for phone fields on patron registration', 'coust', 'label'),
+    oils_i18n_gettext('ui.patron.edit.phone.regex', 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting.', 'coust', 'description'),
+    'string');
+
+COMMIT;
index 6360b1c..3fe5ec1 100755 (executable)
@@ -23,13 +23,27 @@ use Getopt::Long;
 
 my @formats = qw/USMARC UNIMARC XML BRE ARE/;
 
-my ($config,$format,$encoding,$location,$dollarsign,$idl,$help,$holdings,$timeout,$export_mfhd,$type,$all_records) = ('/openils/conf/opensrf_core.xml','USMARC','MARC8','','$',0,undef,undef,0,undef,'biblio',undef);
+my $config = '/openils/conf/opensrf_core.xml';
+my $format = 'USMARC';
+my $encoding = 'MARC8';
+my $location = '';
+my $dollarsign = '$';
+my $idl = 0;
+my $help = undef;
+my $holdings = undef;
+my $timeout = 0;
+my $export_mfhd = undef;
+my $type = 'biblio';
+my $all_records = undef;
+my $replace_001 = undef;
+my @library = ();
 
 GetOptions(
         'help'       => \$help,
         'items'      => \$holdings,
         'mfhd'       => \$export_mfhd,
         'all'        => \$all_records,
+        'replace_001'=> \$replace_001,
         'location=s' => \$location,
         'money=s'    => \$dollarsign,
         'config=s'   => \$config,
@@ -38,6 +52,7 @@ GetOptions(
         'xml-idl=s'  => \$idl,
         'encoding=s' => \$encoding,
         'timeout=i'  => \$timeout,
+        'library=s'  => \@library,
 );
 
 if ($help) {
@@ -66,6 +81,10 @@ Usage: $0 [options]
                     have a lot of items attached to them.
  --type or -t       Record type (BIBLIO, AUTHORITY) [BIBLIO]
  --all or -a        Export all records; ignores input list
+ --library          Export the bibliographic records that have attached
+                    holdings for the listed library or libraries as
+                    identified by shortname
+ --replace_001      Replace the 001 field value with the record ID
 
  Additional options for type = 'BIBLIO':
  --items or -i      Include items (holdings) in the output
@@ -85,10 +104,21 @@ To export a set of MARC21XML authority records in a file named "output.xml"
 for all authority records in the database:
   $0 --format XML --type AUTHORITY --all > output.xml
 
+To export a set of USMARC bibliographic records encoded in UTF-8 in a file
+named "sys1_bibs.mrc" based on records which have attached callnumbers for the
+libraries with the short names "BR1" and "BR2":
+
+  $0 --library BR1 --library BR2 --encoding UTF-8 > sys1_bibs.mrc
+
 HELP
     exit;
 }
 
+if ($all_records && @library) {
+    die('Incompatible arguments: you cannot combine a request for all ' .
+        'records with a request for records by library');
+}
+
 $type = lc($type);
 $format = uc($format);
 $encoding = uc($encoding);
@@ -161,12 +191,39 @@ if ($all_records) {
     for (my $i = 0; $i++ < $top_record;) {
         export_record($i);
     }
+} elsif (@library) {
+    my $recids = $editor->json_query({
+        select => { bre => ['id'] },
+        from => { bre => 'acn' },
+        where => {
+            '+bre' => { deleted => 'f' },
+            '+acn' => { 
+                deleted => 'f', 
+                owning_lib => {
+                    in => {
+                        select => {'aou' => ['id'] },
+                        from => 'aou',
+                        where => { shortname => { in => \@library } }
+                    } 
+                }
+            }
+        },
+        distinct => 1,
+        order_by => [{
+            class => 'bre',
+            field => 'id',
+            direction => 'ASC' 
+        }]
+    });
+
+    foreach my $record (@$recids) {
+        export_record($record->{id});
+    }; 
 } else {
     while ( my $i = <> ) {
         export_record($i);
     }
 }
-
 print "</collection>\n" if ($format eq 'XML');
 
 $speed = $count{did} / (time - $start);
@@ -181,7 +238,7 @@ Total Time Elapsed: $time seconds
 DONE
 
 sub export_record {
-    my $id = shift;
+    my $id = int(shift);
 
     my $bib; 
 
@@ -219,6 +276,16 @@ sub export_record {
             add_bib_holdings($bib, $r);
         }
 
+        if ($replace_001) {
+            my $tcn = $r->field('001');
+            if ($tcn) {
+                $tcn->update($id);
+            } else {
+                my $new_001 = MARC::Field->new('001', $id);
+                $r->insert_fields_ordered($new_001);
+            }
+        }
+
         if ($format eq 'XML') {
             my $xml = $r->as_xml_record;
             $xml =~ s/^<\?.+?\?>$//mo;
index 2aa2e14..2868ef4 100644 (file)
 
 .dijitTextBoxFocused, .dijitFocused { border:1px dashed #3e3e3e; color: #303030; font-weight:bold;}
 
-.hide-non-required tr:not([required]) {
+.hide-non-required tr:not([required]), .hide-non-required tr[required=suggested] {
+    visibility:hidden;
+    display:none;
+}
+
+.hide-non-suggested tr:not([required]) {
     visibility:hidden;
     display:none;
 }
index 5cdc023..ab1150a 100644 (file)
@@ -8,5 +8,6 @@
     "DUPE_PATRON_PHONE" : "Found ${0} patron(s) with the same phone number",
     "DUPE_PATRON_ADDR" : "Found ${0} patron(s) with the same address",
     "REPLACED_ADDRESS" : "<div>Replaces address <b>${0}</b><br/> ${1} ${2}<br/> ${3}, ${4} ${5}</div>",
-    "INVALID_FORM" : "Form is invalid.  Please edit and try again."
+    "INVALID_FORM" : "Form is invalid.  Please edit and try again.",
+    "EXAMPLE" : "Example: "
 }
index f6df5dd..cf227ba 100644 (file)
@@ -29,5 +29,6 @@
     "AUTO_ATTR_VALUE_reset": "Clear / New Patron",
     "AUTO_ATTR_VALUE_pickup": "Pick up",
     "AUTO_ATTR_VALUE_return": "Return",
-    "ADDRESS": "${0}\n${1}\n${2}, ${3} ${4}"
+    "ADDRESS": "${0}\n${1}\n${2}, ${3} ${4}",
+    "COPY_ALERT": "${0}:\n${1}\n\nDo you wish to process it anyway?"
 }
index 1db121a..3fa7735 100644 (file)
@@ -271,6 +271,16 @@ if(!dojo._hasResource['openils.widget.AutoFieldWidget']) {
                     case 'timestamp':
                         dojo.require('dijit.form.DateTextBox');
                         dojo.require('dojo.date.stamp');
+                        if(!this.dijitArgs.constraints) {
+                            this.dijitArgs.constraints = {};
+                        }
+                        if(!this.dijitArgs.constraints.datePattern) {
+                            var user = new openils.User().user;
+                            if(user.ws_ou()) {
+                                var datePattern = fieldmapper.aou.fetchOrgSettingDefault(user.ws_ou(), 'format.date');
+                                if(datePattern) this.dijitArgs.constraints.datePattern = datePattern.value;
+                            }
+                        }
                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
                         if (this.widgetValue != null) {
                             this.widgetValue = openils.Util.timeStampAsDateObj(
index 3e99b28..ecbdf95 100644 (file)
@@ -469,8 +469,8 @@ if(!dojo._hasResource['openils.widget.AutoGrid']) {
                         if(field.widget.widget.attr('disabled')) return;
                         var w = clonePane.fieldList.filter(
                             function(i) { return (i.name == field.name) })[0];
-                        w.widget.baseWidgetValue(field.widget.widgetValue); // sync widgets
-                        w.widget.onload = function(){w.widget.baseWidgetValue(field.widget.widgetValue)}; // async widgets
+                        w.widget.baseWidgetValue(field.widget.widget.attr('value')); // sync widgets
+                        w.widget.onload = function(){w.widget.baseWidgetValue(field.widget.widget.attr('value'))}; // async widgets
                     }
                 );
                 origPane.destroy();
index f82437e..5762458 100644 (file)
@@ -81,21 +81,24 @@ function EligibleLiTable(filter) {
         var count = 0;
         this.reset();
         fieldmapper.standardRequest(
-            ["open-ils.acq", "open-ils.acq.claim.eligible.lineitem_detail"], {
+            ["open-ils.acq", "open-ils.acq.claim.eligible.lineitem_detail.atomic"], {
                 "params": [openils.User.authtoken, this.filter],
                 "async": true,
-                "onresponse": function(r) {
-                    if (r = openils.Util.readResponse(r)) {
-                        if (!count++)
-                            openils.Util.show("acq-eligible-claim-controls");
-                        self.addIfMissing(r.lineitem());
-                    } else {
-                        progressDialog.hide();
-                    }
-                },
-                "oncomplete": function() {
-                    if (count < 1) self.showEmpty();
+                "oncomplete": function(r) {
                     progressDialog.hide();
+                    var rset = openils.Util.readResponse(r);
+                    if (rset.length < 1) self.showEmpty();
+                    else {
+                        var byLi = {};
+                        rset.forEach(
+                            function(r) {
+                                byLi[r.lineitem()] =
+                                    (byLi[r.lineitem()] || 0) + 1;
+                            }
+                        );
+                        for (var key in byLi)
+                            self.addIfMissing(key, byLi[key]);
+                    }
                 }
             }
         );
@@ -113,7 +116,7 @@ function EligibleLiTable(filter) {
             nodeByName("lid_link", "eligible-li-" + liId).onclick =
                 function() {
                     location.href = oilsBasePath + "/acq/po/view/" +
-                        self.liCache[liId].purchase_order().id() + "," +
+                        self.liCache[liId].purchase_order().id() + "/" +
                         liId;
                 };
             openils.Util.show(
@@ -124,10 +127,7 @@ function EligibleLiTable(filter) {
 
     /* Despite being called with an argument that's a lineitem ID, this method
      * is actually called once per lineitem _detail_. */
-    this.addIfMissing = function(liId) {
-        this._updateLidLink(liId);
-        if (this.liCache[liId]) return;
-
+    this.addIfMissing = function(liId, number_of_appearances) {
         var row = dojo.clone(this.rowTemplate);
 
         var checkbox = nodeByName("selector", row);
@@ -141,6 +141,9 @@ function EligibleLiTable(filter) {
                 dojo.attr(row, "id", "eligible-li-" + liId);
                 dojo.attr(checkbox, "value", liId);
                 dojo.place(row, self.tBody, "last");
+
+                for (var i = 0; i < number_of_appearances; i++)
+                    self._updateLidLink(liId);
             }
         );
     };
index c3070c9..4025f5c 100644 (file)
@@ -87,7 +87,62 @@ function load() {
         'ui.patron.default_country',
         'ui.patron.registration.require_address',
         'circ.holds.behind_desk_pickup_supported',
-        'circ.patron_edit.clone.copy_address'
+        'circ.patron_edit.clone.copy_address',
+        'ui.patron.edit.au.second_given_name.show',
+        'ui.patron.edit.au.second_given_name.suggest',
+        'ui.patron.edit.au.suffix.show',
+        'ui.patron.edit.au.suffix.suggest',
+        'ui.patron.edit.au.alias.show',
+        'ui.patron.edit.au.alias.suggest',
+        'ui.patron.edit.au.dob.require',
+        'ui.patron.edit.au.dob.show',
+        'ui.patron.edit.au.dob.suggest',
+        'ui.patron.edit.au.dob.calendar',
+        'ui.patron.edit.au.juvenile.show',
+        'ui.patron.edit.au.juvenile.suggest',
+        'ui.patron.edit.au.ident_value.show',
+        'ui.patron.edit.au.ident_value.suggest',
+        'ui.patron.edit.au.ident_value2.show',
+        'ui.patron.edit.au.ident_value2.suggest',
+        'ui.patron.edit.au.email.require',
+        'ui.patron.edit.au.email.show',
+        'ui.patron.edit.au.email.suggest',
+        'ui.patron.edit.au.email.regex',
+        'ui.patron.edit.au.email.example',
+        'ui.patron.edit.au.day_phone.require',
+        'ui.patron.edit.au.day_phone.show',
+        'ui.patron.edit.au.day_phone.suggest',
+        'ui.patron.edit.au.day_phone.regex',
+        'ui.patron.edit.au.day_phone.example',
+        'ui.patron.edit.au.evening_phone.require',
+        'ui.patron.edit.au.evening_phone.show',
+        'ui.patron.edit.au.evening_phone.suggest',
+        'ui.patron.edit.au.evening_phone.regex',
+        'ui.patron.edit.au.evening_phone.example',
+        'ui.patron.edit.au.other_phone.require',
+        'ui.patron.edit.au.other_phone.show',
+        'ui.patron.edit.au.other_phone.suggest',
+        'ui.patron.edit.au.other_phone.regex',
+        'ui.patron.edit.au.other_phone.example',
+        'ui.patron.edit.phone.regex',
+        'ui.patron.edit.phone.example',
+        'ui.patron.edit.au.active.show',
+        'ui.patron.edit.au.active.suggest',
+        'ui.patron.edit.au.barred.show',
+        'ui.patron.edit.au.barred.suggest',
+        'ui.patron.edit.au.master_account.show',
+        'ui.patron.edit.au.master_account.suggest',
+        'ui.patron.edit.au.claims_returned_count.show',
+        'ui.patron.edit.au.claims_returned_count.suggest',
+        'ui.patron.edit.au.claims_never_checked_out_count.show',
+        'ui.patron.edit.au.claims_never_checked_out_count.suggest',
+        'ui.patron.edit.au.alert_message.show',
+        'ui.patron.edit.au.alert_message.suggest',
+        'ui.patron.edit.aua.post_code.regex',
+        'ui.patron.edit.aua.post_code.example',
+        'ui.patron.edit.aua.county.require',
+        'format.date',
+        'ui.patron.edit.default_suggested'
     ]);
 
     for(k in orgSettings)
@@ -121,6 +176,9 @@ function load() {
 
     tbody = dojo.byId('uedit-tbody');
 
+    if(orgSettings['ui.patron.edit.default_suggested'])
+        uEditToggleRequired(2);
+
     addrTemplateRows = dojo.query('tr[type=addr-template]', tbody);
     dojo.forEach(addrTemplateRows, function(row) { row.parentNode.removeChild(row); } );
     statCatTemplate = tbody.removeChild(dojo.byId('stat-cat-row-template'));
@@ -591,6 +649,7 @@ function fleshFMRow(row, fmcls, args) {
     var wconstraints = row.getAttribute('wconstraints');
     /* use CSS to set the zindex for widgets you want to disable. */
     var disabled = dojo.style(row, 'zIndex') == -1 ? true : false;
+    var isphone = (fmcls == 'au') && (fmfield.search('_phone') != -1);
 
     var isPasswd2 = (fmfield == 'passwd2');
     if(isPasswd2) fmfield = 'passwd';
@@ -601,6 +660,7 @@ function fleshFMRow(row, fmcls, args) {
     var htd = existing[0] || row.appendChild(document.createElement('td'));
     var ltd = existing[1] || row.appendChild(document.createElement('td'));
     var wtd = existing[2] || row.appendChild(document.createElement('td'));
+    var ftd = existing[3] || row.appendChild(document.createElement('td'));
 
     openils.Util.addCSSClass(htd, 'uedit-help');
     if(fieldDoc[fmcls] && fieldDoc[fmcls][fmfield]) {
@@ -612,11 +672,22 @@ function fleshFMRow(row, fmcls, args) {
     }
 
     if(!ltd.textContent) {
-        var span = document.createElement('span');
         ltd.appendChild(document.createTextNode(fieldIdl.label));
     }
 
-    span = document.createElement('span');
+    if(!ftd.textContent) {
+        if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.example']) {
+            ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.example']));
+        }
+        else if(isphone && orgSettings['ui.patron.edit.phone.example']) {
+            ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + orgSettings['ui.patron.edit.phone.example']));
+        }
+        else if(fieldIdl.datatype == 'timestamp') {
+            ftd.appendChild(document.createTextNode(localeStrings.EXAMPLE + dojo.date.locale.format(new Date(1970,0,31),{selector: "date", fullYear: true, datePattern: (orgSettings['format.date'] ? orgSettings['format.date'] : null)})));
+        }
+    }
+
+    var span = document.createElement('span');
     wtd.appendChild(span);
 
     var fmObject = null;
@@ -631,7 +702,19 @@ function fleshFMRow(row, fmcls, args) {
             break;
     }
 
-    var required = row.getAttribute('required') == 'required';
+    // Adjust required value by org settings
+    var curRequired = row.getAttribute('required');
+    var required = curRequired == 'required';
+    if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.require']) {
+        row.setAttribute('required', 'required');
+        required = true;
+    }
+    else if (curRequired != 'required' && orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.show']) {
+        row.setAttribute('required', 'show');
+    }
+    else if (curRequired != 'required' && curRequired != 'show' && orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.suggest']) {
+        row.setAttribute('required', 'suggested');
+    }
 
     // password data is not fetched/required/displayed for existing users
     if(!patron.isnew() && 'passwd' == fmfield)
@@ -644,14 +727,21 @@ function fleshFMRow(row, fmcls, args) {
         disabled : disabled
     };
 
+    // Org settings provided regex?
+    if(orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.regex']) {
+        dijitArgs.regExp = orgSettings['ui.patron.edit.' + fmcls + '.' + fmfield + '.regex'];
+    }
+    else if(isphone && orgSettings['ui.patron.edit.phone.regex']) {
+        dijitArgs.regExp = orgSettings['ui.patron.edit.phone.regex'];
+    }
+
     if(fmcls == 'au' && fmfield == 'passwd') {
         if (orgSettings['global.password_regex']) {
             dijitArgs.regExp = orgSettings['global.password_regex'];
         }
     }
 
-    // TODO RSN: Add Setting!
-    if(fmcls == 'au' && fmfield == 'dob')
+    if(fmcls == 'au' && fmfield == 'dob' && !orgSettings['ui.patron.edit.au.dob.calendar'])
         dijitArgs.popupClass = "";
 
     var value = row.getAttribute('wvalue');
@@ -913,7 +1003,7 @@ function attachWidgetEvents(fmcls, fmfield, widget) {
                 return;
 
             case 'day_phone':
-                // if configured, use the last for digits of the day phone number as the password
+                // if configured, use the last four digits of the day phone number as the password
                 if(uEditUsePhonePw && patron.isnew()) {
                     dojo.connect(widget.widget, 'onChange',
                         function(newVal) {
@@ -1498,13 +1588,29 @@ function uEditDeleteAddr(id, noAlert) {
     widgetPile = widgetPile.filter(function(w){return (w._addr != id)});
 }
 
-function uEditToggleRequired() {
-    if((tbody.className +'').match(/hide-non-required/)) 
-        openils.Util.removeCSSClass(tbody, 'hide-non-required');
-    else
-        openils.Util.addCSSClass(tbody, 'hide-non-required');
-    openils.Util.toggle('uedit-show-required');
-    openils.Util.toggle('uedit-show-all');
+function uEditToggleRequired(level) {
+    openils.Util.removeCSSClass(tbody, 'hide-non-required');
+    openils.Util.removeCSSClass(tbody, 'hide-non-suggested');
+    openils.Util.show('uedit-show-required');
+    openils.Util.show('uedit-show-required-br');
+    openils.Util.show('uedit-show-suggested');
+    openils.Util.show('uedit-show-suggested-br');
+    openils.Util.show('uedit-show-all');
+    switch(level) {
+        case 1:
+            openils.Util.hide('uedit-show-required');
+            openils.Util.hide('uedit-show-required-br');
+            openils.Util.addCSSClass(tbody, 'hide-non-required');
+            break;
+        case 2:
+            openils.Util.hide('uedit-show-suggested');
+            openils.Util.hide('uedit-show-suggested-br');
+            openils.Util.addCSSClass(tbody, 'hide-non-suggested');
+            break;
+        default:
+            openils.Util.hide('uedit-show-all');
+            break;
+    } 
 }
 
 function printable_output() {
index 9822144..1eeb7cb 100644 (file)
@@ -4,6 +4,7 @@
 dojo.require("dojo.data.ItemFileReadStore");
 dojo.require("dojo.date.locale");
 dojo.require("openils.PermaCrud");
+dojo.require("dojo.string");
 
 function Populator(widgets, primary_input) {
     this.widgets = widgets;
@@ -133,7 +134,7 @@ Populator.prototype.populate_patron = function(data) {
     this.reveal_container(this.widgets.patron);
     /* Maybe add patron's home OU or something here later... */
 };
-Populator.prototype.return_by_resource = function(barcode) {
+Populator.prototype.return_by_resource = function(barcode, override) {
     /* XXX instead of talking to the server every time we do this, we could
      * also check the "out" cache, iff we have one.  */
     var r = fieldmapper.standardRequest(
@@ -157,14 +158,26 @@ Populator.prototype.return_by_resource = function(barcode) {
             alert(localeStrings.NOTICE_CHANGE_OF_PATRON);
         }
         this.patron_barcode = new_barcode;
-        var ret = this.return(r);
+        var ret = this.return(r, override);
         if (!ret) {
             alert(localeStrings.RETURN_NO_RESPONSE);
         } else if (is_ils_event(ret) && ret.textcode != "SUCCESS") {
-            if (ret.textcode == "ROUTE_ITEM")
+            if (ret.textcode == "ROUTE_ITEM") {
                 display_transit_slip(ret);
-            else
+            } else if (ret.textcode == "COPY_ALERT_MESSAGE") {
+                if (
+                    confirm(
+                        dojo.string.substitute(
+                            localeStrings.COPY_ALERT, [ret.desc, ret.payload]
+                       )
+                    )
+                ) {
+                    this.return_by_resource(barcode, true /*override*/);
+                    return;
+                }
+            } else {
                 alert(my_ils_error(localeStrings.RETURN_ERROR, ret));
+            }
         } else {
             /* XXX speedbump should go, but something has to happen else
              * there's no indication to staff that anything happened when
@@ -231,9 +244,11 @@ Populator.prototype.pickup = function(reservation) {
         }]
     );
 };
-Populator.prototype.return = function(reservation) {
+Populator.prototype.return = function(reservation, override) {
+    var method = "open-ils.circ.reservation.return";
+    if (override) method += ".override";
     return fieldmapper.standardRequest(
-        ["open-ils.circ", "open-ils.circ.reservation.return"],
+        ["open-ils.circ", method],
         [openils.User.authtoken, {
             "patron_barcode": this.patron_barcode,
             "reservation": reservation.id()
@@ -258,20 +273,33 @@ Populator.prototype.act_on_selected = function(how, which) {
     var reservations = selected_id_list.map(function(o) { return cache[o]; });
 
     /* Do we have to process these one at a time?  I think so... */
-    for (var i in reservations) {
-        var result = this[how](reservations[i]);
+    var self = this;
+    function looper(reservation, override) {
+        if (looper._done) return;
+        var result = self[how](reservation, override);
         if (!result) {
             alert(no_response_msg);
         } else if (is_ils_event(result) && result.textcode != "SUCCESS") {
-            if (result.textcode == "ROUTE_ITEM")
+            if (result.textcode == "ROUTE_ITEM") {
                 display_transit_slip(result);
-            else
+            } else if (result.textcode == "COPY_ALERT_MESSAGE") {
+                if (confirm(
+                    dojo.string.substitute(
+                        localeStrings.COPY_ALERT, [result.desc, result.payload]
+                   )
+                )) {
+                    looper(reservation, true);
+                }
+                return; // continues processing other resvs
+            } else {
                 alert(my_ils_error(error_msg, result));
+            }
         } else {
-            continue;
+            return;
         }
-        break;
+        looper._done = true;
     }
+    dojo.forEach(reservations, looper);
 
     this.populate();
 };
index 054e56a..5e2b66b 100644 (file)
@@ -360,11 +360,15 @@ var FETCH_ISSUANCE                                        = 'open-ils.serial:open-ils.serial.issuance.pub_fleshed.b
 var FETCH_COPY_LOCATIONS               = 'open-ils.circ:open-ils.circ.copy_location.retrieve.all';
 var FETCH_COPY_NOTES                           = 'open-ils.circ:open-ils.circ.copy_note.retrieve.all';
 var FETCH_COPY_STAT_CATS               = 'open-ils.circ:open-ils.circ.asset.stat_cat_entries.fleshed.retrieve_by_copy';
-var FETCH_LIT_FORMS                            = 'open-ils.search:open-ils.search.biblio.lit_form_map.retrieve.all';
-var FETCH_ITEM_FORMS                           = 'open-ils.search:open-ils.search.biblio.item_form_map.retrieve.all';
-var FETCH_ITEM_TYPES                           = 'open-ils.search:open-ils.search.biblio.item_type_map.retrieve.all';
-var FETCH_BIB_LEVELS                           = 'open-ils.search:open-ils.search.biblio.bib_level_map.retrieve.all';
-var FETCH_AUDIENCES                            = 'open-ils.search:open-ils.search.biblio.audience_map.retrieve.all';
+
+/* XXX deprecated.  Use ccvm's instead  */
+var FETCH_LIT_FORMS             = 'open-ils.search:open-ils.search.biblio.lit_form_map.retrieve.all';
+var FETCH_ITEM_FORMS            = 'open-ils.search:open-ils.search.biblio.item_form_map.retrieve.all';
+var FETCH_ITEM_TYPES            = 'open-ils.search:open-ils.search.biblio.item_type_map.retrieve.all';
+var FETCH_BIB_LEVELS            = 'open-ils.search:open-ils.search.biblio.bib_level_map.retrieve.all';
+var FETCH_AUDIENCES             = 'open-ils.search:open-ils.search.biblio.audience_map.retrieve.all';
+/* ----------------------------------- */
+
 //var FETCH_HOLD_STATUS                        = 'open-ils.circ:open-ils.circ.hold.status.retrieve';
 var FETCH_HOLD_STATUS                  = 'open-ils.circ:open-ils.circ.hold.queue_stats.retrieve';
 var FETCH_NON_CAT_CIRCS                        = 'open-ils.circ:open-ils.circ.open_non_cataloged_circulation.user';
index 6a878e5..3e6a9db 100644 (file)
@@ -12,7 +12,7 @@
 <!ENTITY conify.admin.grp_tree.link "Permission Groups">
 <!ENTITY conify.admin.perm_list.link "Permissions">
 <!ENTITY conify.admin.copy_status.link "Copy Statuses">
-<!ENTITY conify.admin.marc_code_maps.link "MARC Codes">
+<!ENTITY conify.admin.marc_code_maps.link "Coded Value Maps">
 <!ENTITY conify.admin.bottom_pane.title "Controls">
 <!-- Organization unit type configuration interface -->
 <!ENTITY conify.org_unit_type.title "Global :: Actor :: Organization Unit Types">
index a723b5f..07e5404 100644 (file)
 <!ENTITY staff.main.menu.admin.server_admin.conify.grp_tree.label "Permission Groups">
 <!ENTITY staff.main.menu.admin.server_admin.conify.perm_list.label "Permissions">
 <!ENTITY staff.main.menu.admin.server_admin.conify.copy_status.label "Copy Statuses">
-<!ENTITY staff.main.menu.admin.server_admin.conify.marc_code_maps.label "MARC Codes">
+<!ENTITY staff.main.menu.admin.server_admin.conify.marc_code_maps.label "Coded Value Maps">
 <!ENTITY staff.main.menu.admin.server_admin.conify.billing_type.label "Billing Types">
 <!ENTITY staff.main.menu.admin.server_admin.conify.z3950_source.label "Z39.50 Servers">
 <!ENTITY staff.main.menu.admin.server_admin.conify.circulation_modifier.label "Circulation Modifiers">
 <!ENTITY staff.server.admin.org_unit_settings.title "Organization Unit Settings">
 <!ENTITY staff.server.admin.org_unit_settings.context "Context Location">
 <!ENTITY staff.server.admin.org_unit_settings.uninherited " * Indicates the setting is not inherited from the parent org unit at run time">
+<!ENTITY staff.server.admin.org_unit_settings.no_perms "You do not have permission to view org unit settings">
 <!ENTITY staff.server.admin.org_unit_settings.edit_setting "Edit Setting">
 <!ENTITY staff.server.admin.org_unit_settings.delete_setting "Delete Setting">
 <!ENTITY staff.server.admin.org_unit_settings.update_setting "Update Setting">
index 5f9fbf7..2f7ed86 100644 (file)
                                                                <td align=''>
                                                                        <select id='adv_global_sort_by' onchange='__setsortsel();'>
                                                                                <option value='rel'>&advanced.relevance;</option>
-                                                                               <option value='title'>&common.title;</option>
-                                                                               <option value='author'>&common.author;</option>
+                                                                               <option value='titlesort'>&common.title;</option>
+                                                                               <option value='authorsort'>&common.author;</option>
                                                                                <option value='pubdate'>&advanced.pubdate;</option>
                                                                        </select>
                                                                </td>
index 2057fdf..71cf76a 100644 (file)
@@ -6,12 +6,12 @@
                        <td><select id='opac.result.sort' onchange='searchBarSubmit(true);'>
                                <option selected='selected' value=''>&result.sort_by;</option>
                                <optgroup label='&result.sort_by.title;'>
-                                       <option id='opac.result.title.a2z' label='&common.a2z.titla;' value='title.asc'>&common.a2z.title;</option>
-                                       <option id='opac.result.title.z2a' label='&common.z2a.titla;' value='title.desc'>&common.z2a.title;</option>
+                                       <option id='opac.result.title.a2z' label='&common.a2z.titla;' value='titlesort.asc'>&common.a2z.title;</option>
+                                       <option id='opac.result.title.z2a' label='&common.z2a.titla;' value='titlesort.desc'>&common.z2a.title;</option>
                                </optgroup>
                                <optgroup label='&result.sort_by.author;'>
-                                       <option id='opac.result.author.a2z' label='&common.a2z.author;' value='author.asc'>&common.a2z.author;</option>
-                                       <option id='opac.result.author.z2a' label='&common.z2a.author;' value='author.desc'>&common.z2a.author;</option>
+                                       <option id='opac.result.author.a2z' label='&common.a2z.author;' value='authorsort.asc'>&common.a2z.author;</option>
+                                       <option id='opac.result.author.z2a' label='&common.z2a.author;' value='authorsort.desc'>&common.z2a.author;</option>
                                </optgroup>
                                <optgroup label='&result.sort_by.pubdate;'>
                                        <option id='opac.result.pubdate.new2old' label='&common.new2old.pubdate;' 
@@ -22,4 +22,4 @@
                        </select></td>
                </tr>
        </table>
-</span>
\ No newline at end of file
+</span>
index 5cacdda..a6311b7 100644 (file)
@@ -14,27 +14,12 @@ function advgInit() {
 
        $n($('adv_global_tbody'), 'term').focus();
 
-       var extras = [ 
-               FETCH_LIT_FORMS, 
-               FETCH_ITEM_FORMS, 
-               FETCH_ITEM_TYPES, 
-               FETCH_AUDIENCES,
-               FETCH_BIB_LEVELS 
-    ];
-
-       for( var x in extras ) {
-
-               var req = new Request(extras[x]);
-
-               if(x == 0) req.request.sel = $('adv_global_lit_form');
-               if(x == 1) req.request.sel = $('adv_global_item_form');
-               if(x == 2) req.request.sel = $('adv_global_item_type');
-               if(x == 3) req.request.sel = $('adv_global_audience');
-               if(x == 4) req.request.sel = $('adv_global_bib_level');
-
-               req.callback(advDrawBibExtras);
-               req.send();
-       }
+    var ctypes = ["bib_level", "item_form", "item_type", "audience", "lit_form"];
+
+    var req = new Request('open-ils.fielder:open-ils.fielder.ccvm.atomic', {"cache":1,"query":{"ctype":ctypes}});
+    req.callback(advDrawBibExtras);
+    req.request.ctypes = ctypes;
+    req.send();
 
        var input = $n($('adv_global_trow'), 'term');
        input.focus();
@@ -158,21 +143,24 @@ function advAddGblRow() {
 
 function advDrawBibExtras(r) {
        var data = r.getResultObject();
-       var sel = r.sel;
-
-       data = data.sort( /* sort alphabetically */
-               function(a,b) { 
-                       if( a.value() < b.value() ) return -1;
-                       if( a.value() > b.value() ) return 1;
-                       return 0;
-               }
-       );
-
-       for( var d = 0; d < data.length; d++ ) {
-               var thing = data[d];
-               var opt = insertSelectorVal( sel, -1, thing.value(), thing.code() );
-               opt.setAttribute('title', thing.value());
-       }
+    var ctypes = r.ctypes
+    dojo.forEach(ctypes,
+        function(ctype) {
+               var sel = $('adv_global_' + ctype);
+            var ctypeData = dojo.filter(data, function(item) { return item.ctype == ctype } );
+            ctypeData = ctypeData.sort(
+                function(a,b) { /* sort alphabetically */
+                    return (a.value < b.value) ? -1 : 1;
+                }
+            );
+            dojo.forEach(ctypeData,
+                function(thing) {
+                    var opt = insertSelectorVal(sel, -1, thing.value, thing.code);
+                    opt.setAttribute('title', thing.value);
+                }
+            );
+        }
+    );
 }
 
 function advSelToStringList(sel) {
index 12af197..6fd49f9 100644 (file)
@@ -285,10 +285,11 @@ function myOPACDrawCheckedTitle(r) {
        var record = r.getResultObject();
        var circid = r.circ;
 
-       if(!record || checkILSEvent(record)) {
+       /* Draw pre-cataloged items */
+       if(!record || checkILSEvent(record) || !(record.title() && record.author())) {
                var req = new Request( FETCH_COPY, r.copy );
-               req.request.circ = circid
-               req.callback(myOPACDrawNonCatalogedItem);
+               req.request.circ = circid;
+               req.callback(myOPACDrawPreCatalogedItem);
                req.send();
                return;
        }
@@ -296,12 +297,13 @@ function myOPACDrawCheckedTitle(r) {
        var row = $('myopac_checked_row_ ' + circid);
        var tlink = $n( row, "myopac_checked_title_link" );
        var alink = $n( row, "myopac_checked_author_link" );
+
        buildTitleDetailLink(record, tlink);
        buildSearchLink(STYPE_AUTHOR, record.author(), alink);
    __circ_titles[circid] = record.title();
 }
 
-function myOPACDrawNonCatalogedItem(r) {
+function myOPACDrawPreCatalogedItem(r) {
        var copy = r.getResultObject();
        var circid = r.circ;
 
index cf186e7..865aa59 100644 (file)
                                                                        <td align=''>
                                                                                <select id='adv_global_sort_by' onchange='__setsortsel();'>
                                                                                        <option value='rel'>&advanced.relevance;</option>
-                                                                                       <option value='title'>&common.title;</option>
-                                                                                       <option value='author'>&common.author;</option>
+                                                                                       <option value='titlesort'>&common.title;</option>
+                                                                                       <option value='authorsort'>&common.author;</option>
                                                                                        <option value='pubdate'>&advanced.pubdate;</option>
                                                                                </select>
                                                                        </td>
index 27eb382..c8b3327 100644 (file)
@@ -3,12 +3,12 @@
     <select id='opac.result.sort' onchange='searchBarSubmit(true);'>
         <option selected='selected' value=''>&result.sort_by;</option>
         <optgroup label='&result.sort_by.title;'>
-            <option id='opac.result.title.a2z' label='&common.a2z.titla;' value='title.asc'>&common.a2z.title;</option>
-            <option id='opac.result.title.z2a' label='&common.z2a.titla;' value='title.desc'>&common.z2a.title;</option>
+            <option id='opac.result.title.a2z' label='&common.a2z.titla;' value='titlesort.asc'>&common.a2z.title;</option>
+            <option id='opac.result.title.z2a' label='&common.z2a.titla;' value='titlesort.desc'>&common.z2a.title;</option>
         </optgroup>
         <optgroup label='&result.sort_by.author;'>
-            <option id='opac.result.author.a2z' label='&common.a2z.author;' value='author.asc'>&common.a2z.author;</option>
-            <option id='opac.result.author.z2a' label='&common.z2a.author;' value='author.desc'>&common.z2a.author;</option>
+            <option id='opac.result.author.a2z' label='&common.a2z.author;' value='authorsort.asc'>&common.a2z.author;</option>
+            <option id='opac.result.author.z2a' label='&common.z2a.author;' value='authorsort.desc'>&common.z2a.author;</option>
         </optgroup>
         <optgroup label='&result.sort_by.pubdate;'>
             <option id='opac.result.pubdate.new2old' label='&common.new2old.pubdate;' 
index b843e8c..aa1dad5 100644 (file)
@@ -18,8 +18,9 @@
     <button dojoType='dijit.form.Button' jsId='saveButton' onClick='uEditSave' scrollOnFocus='false'>Save</button>
     <button dojoType='dijit.form.Button' jsId='saveCloneButton' onClick='uEditSaveClone' scrollOnFocus='false'>Save &amp; Clone</button>
     <div id='require-toggle'>
-        <a href='javascript:uEditToggleRequired();' id='uedit-show-required'>Show Only Required Fields</a>
-        <a href='javascript:uEditToggleRequired();' id='uedit-show-all' class='hidden'>Show All Fields</a>
+        <a href='javascript:uEditToggleRequired(1);' id='uedit-show-required'>Show Only Required Fields</a><br id='uedit-show-required-br'/>
+        <a href='javascript:uEditToggleRequired(2);' id='uedit-show-suggested'>Show Suggested Fields</a><br id='uedit-show-suggested-br'/>
+        <a href='javascript:uEditToggleRequired(0);' id='uedit-show-all' class='hidden'>Show All Fields</a>
     </div>
 </div>
 
index 4e9fcbe..30eeb5c 100644 (file)
     </tr>
 
     <!-- stat cats -->
-    <tr class='divider' id='stat-cat-divider'><td colspan='0'>Statistical Categories</td></tr>
-    <tr id='stat-cat-row-template'><td class='uedit-help'/><td name='name'/><td name='widget'/></tr>
+    <tr class='divider' id='stat-cat-divider' required='suggested'><td colspan='0'>Statistical Categories</td></tr>
+    <tr id='stat-cat-row-template'><td class='uedit-help' required='suggested'/><td name='name'/><td name='widget'/></tr>
 
     <!-- surveys -->
-    <tr id='survey-row-template' class='divider'><td colspan='0' name='name'/></tr>
-    <tr id='survey-question-row-template'><td class='uedit-help'/><td name='question'/><td name='answers'/></tr>
+    <tr id='survey-row-template' class='divider' required='suggested'><td colspan='0' name='name'/></tr>
+    <tr id='survey-question-row-template'><td class='uedit-help' required='suggested'/><td name='question'/><td name='answers'/></tr>
 </tbody>
 
diff --git a/Open-ILS/web/templates/default/conify/global/config/coded_value_map.tt2 b/Open-ILS/web/templates/default/conify/global/config/coded_value_map.tt2
new file mode 100644 (file)
index 0000000..592e539
--- /dev/null
@@ -0,0 +1,63 @@
+[% WRAPPER default/base.tt2 %]
+<h1>Code Value Maps</h1> <br/>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client" class='oils-header-panel'>
+    <div>Coded Value Maps</div>
+    <div>
+        <button dojoType='dijit.form.Button' onClick='ccvmGrid.showCreateDialog()'>New Map</button>
+        <button dojoType='dijit.form.Button' onClick='ccvmGrid.deleteSelected()'>Delete Selected</button>
+    </div>
+</div>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <span>Record Attribute Type: </span><div id='attr-def-div'/>
+</div>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <table  jsId="ccvmGrid"
+            autoHeight='true'
+            dojoType="openils.widget.AutoGrid"
+            fieldOrder="['ctype', 'code', 'value', 'description']"
+            query="{code: '*'}"
+            defaultCellWidth='"25%"'
+            fmClass='ccvm'
+            showPaginator='true'
+            editOnEnter='true'/>
+ </div>
+
+<script type ="text/javascript">
+
+    dojo.require('dijit.form.FilteringSelect');
+    dojo.require('openils.widget.AutoGrid');
+    dojo.require('openils.widget.AutoFieldWidget');
+
+    openils.Util.addOnLoad(
+        function() {
+
+            var selector = new openils.widget.AutoFieldWidget({
+                fmClass : 'ccvm',
+                fmField : 'ctype',
+                parentNode : dojo.byId('attr-def-div')
+            });
+
+            selector.build(
+                function(w, ww) {
+                    dojo.connect(w, 'onChange', 
+                        function(newVal) {
+                            ccvmGrid.resetStore();
+                            ccvmGrid.loadAll({order_by : {ccvm : 'code'}}, {ctype : newVal});
+                            ccvmGrid.overrideWidgetArgs.ctype = {dijitArgs : {value : newVal}};
+                            // ^-- why is this not working?
+                        }
+                    );
+                }
+            );
+
+            // hide the progress indicator since we're not loading any data up front
+            dojo.style(ccvmGrid.loadProgressIndicator, 'visibility', 'hidden');
+        }
+    );
+
+</script>
+
+[% END %]
diff --git a/Open-ILS/web/templates/default/conify/global/config/record_attr_definition.tt2 b/Open-ILS/web/templates/default/conify/global/config/record_attr_definition.tt2
new file mode 100644 (file)
index 0000000..3b20241
--- /dev/null
@@ -0,0 +1,30 @@
+[% WRAPPER default/base.tt2 %]
+<h1>Record Attribute Definitions</h1> <br/>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client" class='oils-header-panel'>
+    <div>Record Attribute Definitions</div>
+    <div>
+        <button dojoType='dijit.form.Button' onClick='cradGrid.showCreateDialog()'>New Attr. Definition</button>
+        <button dojoType='dijit.form.Button' onClick='cradGrid.deleteSelected()'>Delete Selected</button>
+    </div>
+</div>
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <table  jsId="cradGrid"
+            autoHeight='true'
+            dojoType="openils.widget.AutoGrid"
+            fieldOrder="['name', 'label']",
+            query="{name: '*'}"
+            fmClass='crad'
+            showPaginator='true'
+            editOnEnter='true'/>
+ </div>
+
+<script type ="text/javascript">
+    dojo.require('openils.widget.AutoGrid');
+    openils.Util.addOnLoad(
+        function() { cradGrid.loadAll({order_by : {crad : 'name'}}) }
+    );
+</script>
+
+[% END %]
index b6a02e0..fe74bdd 100644 (file)
@@ -9,7 +9,7 @@ export STAFF_CLIENT_STAMP_ID = $$(/bin/cat build/STAMP_ID)
 
 # from http://closure-compiler.googlecode.com/files/compiler-latest.zip  FIXME: Autotools this?
 export CLOSURE_COMPILER_JAR = ~/closure-compiler/compiler.jar
-XULRUNNER_VERSION=1.9.2.13
+XULRUNNER_VERSION=1.9.2.15
 XULRUNNER_WINFILE=xulrunner-$(XULRUNNER_VERSION).en-US.win32.zip
 XULRUNNER_LINUXFILE=xulrunner-$(XULRUNNER_VERSION).en-US.linux-i686.tar.bz2
 XULRUNNER_URL=http://releases.mozilla.org/pub/mozilla.org/xulrunner/releases/$(XULRUNNER_VERSION)/runtimes/
index f637bad..978be55 100644 (file)
@@ -218,7 +218,7 @@ OpenILS.data.prototype = {
 
         'pgt' : [ api.FM_PGT_RETRIEVE.app, api.FM_PGT_RETRIEVE.method, [], true ],
         'cit' : [ api.FM_CIT_RETRIEVE.app, api.FM_CIT_RETRIEVE.method, [], true ],
-        'citm' : [ api.FM_CITM_RETRIEVE.app, api.FM_CITM_RETRIEVE.method, [], true ],
+        'citm' : [ api.FM_CITM_RETRIEVE.app, api.FM_CITM_RETRIEVE.method, [{'query':{'ctype' : 'item_type'}}], true ],
         /*
         'cst' : [ api.FM_CST_RETRIEVE.app, api.FM_CST_RETRIEVE.method, [], true ],
         */
index d7fa588..8e8663e 100644 (file)
@@ -237,7 +237,7 @@ var api = {
     'FM_CIRC_BACKDATE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.post_checkin_backdate' },
     'FM_CIRC_BACKDATE_BATCH' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.post_checkin_backdate.batch' },
     'FM_CIT_RETRIEVE' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.ident_types.retrieve', 'secure' : false },
-    'FM_CITM_RETRIEVE' : { 'app' : 'open-ils.search', 'method' : 'open-ils.search.biblio.item_type_map.retrieve.all', 'secure' : false },
+    'FM_CITM_RETRIEVE' : { 'app' : 'open-ils.fielder', 'method' : 'open-ils.fielder.ccvm.atomic', 'secure' : false },
     'FM_CNAL_RETRIEVE' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.net_access_level.retrieve.all', 'secure' : false },
     'FM_CNCT_RETRIEVE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.non_cat_types.retrieve.all', 'secure' : false },
     'FM_CRAHP_RETRIEVE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.config.rules.age_hold_protect.retrieve.all', 'secure' : false },
index 3d7eda8..f63ad8e 100644 (file)
@@ -730,7 +730,7 @@ main.menu.prototype = {
             ],
             'cmd_server_admin_marc_code' : [
                 ['oncommand'],
-                function() { open_conify_page('config/marc_code_maps', null); }
+                function() { open_eg_web_page('conify/global/config/record_attr_definition'); }
             ],
             'cmd_server_admin_billing_type' : [
                 ['oncommand'],
index b275eb7..82f0475 100644 (file)
@@ -29,10 +29,20 @@ function osInit(data) {
     contextOrg = user.user.ws_ou();
     openils.User.authtoken = authtoken;
 
-    var connect = function() { dojo.connect(contextOrg, 'onChange', osChangeContext); };
-    new openils.User().buildPermOrgSelector('STAFF_LOGIN', osContextSelector, null, connect);
+    var connect = function() { 
+        dojo.connect(contextOrg, 'onChange', osChangeContext); 
+
+        // don't draw the org settings grid unless the user has permission
+        // to view org settings in at least 1 org unit
+        osContextSelector.store.fetch({query: {}, start: 0, count: 0, 
+            onBegin: function(size) { 
+                if(size) { osDraw();  return; }
+                dojo.removeClass('no-perms', 'hide_me');
+            }
+        });
+    };
 
-    osDraw();
+    new openils.User().buildPermOrgSelector('VIEW_ORG_SETTINGS', osContextSelector, null, connect);
 }
 dojo.addOnLoad(osInit);
 
index 2862fba..017ab39 100644 (file)
@@ -40,6 +40,9 @@
                 </span>
                 <span>&staff.server.admin.org_unit_settings.uninherited;</span>
             </div>
+            <div dojoType="dijit.layout.ContentPane" layoutAlign='client'>
+                <div class='hide_me' id='no-perms'><b>&staff.server.admin.org_unit_settings.no_perms;</b></div>
+            </div>
             <div dojoType="dijit.layout.ContentPane" layoutAlign='client' class='tall'>
                 <script>
                     var osGridLayout = [{
index 7ea3948..8680351 100644 (file)
@@ -888,8 +888,8 @@ g.panes_and_field_names = {
      [
         $('catStrings').getString('staff.cat.copy_editor.field.circulate_as_type.label'),
         {     
-            render: 'fm.circ_as_type() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : g.data.hash.citm[ fm.circ_as_type() ].value()',
-            input: 'c = function(v){ g.apply("circ_as_type",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.remove_circulate_as_type"), "<HACK:KLUDGE:NULL>" ] ].concat( util.functional.map_list( g.data.list.citm, function(n){return [ n.code() + " - " + n.value(), n.code()];} ).sort() ) ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
+            render: 'fm.circ_as_type() == null ? $("catStrings").getString("staff.cat.copy_editor.field.unset_or_null") : g.data.hash.citm[ fm.circ_as_type() ].value',
+            input: 'c = function(v){ g.apply("circ_as_type",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( [ [ $("catStrings").getString("staff.cat.copy_editor.remove_circulate_as_type"), "<HACK:KLUDGE:NULL>" ] ].concat( util.functional.map_list( g.data.list.citm, function(n){return [ n.code + " - " + n.value, n.code];} ).sort() ) ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
         } 
     ],
     [
index 08c18a0..d5b1ff5 100644 (file)
@@ -188,6 +188,7 @@ staff.main.gen_offline_widgets.workstation_registration_denied=You do not have p
 staff.main.gen_offline_widgets.registration.override=Override Registration Failure?
 staff.main.gen_offline_widgets.registration.error=Workstation Registration error (%1$s)
 staff.main.gen_offline_widgets.registration.success=Registration successful
+staff.main.gen_offline_widgets.registration.empty=Please specify a workstation name
 staff.util.timestamp_dialog.future_date_disallowed=Future dates disallowed.
 staff.util.timestamp_dialog.past_date_disallowed=Past dates disallowed.
 staff.util.timestamp_dialog.today_disallowed=Today disallowed.
index 27f5cb4..1bd51bf 100644 (file)
         }
 
         function register() {
+            if(g.tb.value.length == 0) {
+                alert(document.getElementById('commonStrings').getString('staff.main.gen_offline_widgets.registration.empty'));
+                return;
+            }
             try {
                 var robj = g.network.simple_request(
                     'AUTH_WORKSTATION',
index 0be012d..67252b1 100644 (file)
@@ -769,7 +769,21 @@ function apply_payment() {
         payment_blob.payment_type = $('payment_type').value;
         var tally_blob = tally_pending();
         payment_blob.payments = tally_blob.payments;
-        payment_blob.patron_credit = $('convert_change_to_credit').checked ? tally_blob.change : '0.00'; 
+        // Handle patron credit
+        if ( payment_blob.payment_type == 'credit_payment' ) { // paying with patron credit
+            if ( $('convert_change_to_credit').checked ) {
+                // No need to convert credit into credit, handled automatically
+                payment_blob.patron_credit = '0.00';
+            } else {
+                // Cashing out extra credit as change
+                payment_blob.patron_credit = 0 - tally_blob.change;
+            }
+        } else if ( $('convert_change_to_credit').checked ) {
+            // Saving change from a non-credit payment as patron credit on server
+            payment_blob.patron_credit = tally_blob.change;
+        } else {
+            payment_blob.patron_credit = '0.00';
+        }
         if ( payment_blob.payments.length == 0 && payment_blob.patron_credit == '0.00' ) {
             alert($("patronStrings").getString('staff.patron.bills.apply_payment.nothing_applied'));
             return;
diff --git a/README b/README
index cc8edf4..8644216 100644 (file)
--- a/README
+++ b/README
@@ -111,27 +111,27 @@ connection information in opensrf.xml for you.
 Creating the Evergreen database:
 ===============================
 
-PostgreSQL 8.3 or 8.4 will be installed on your system by the Makefile.install
+PostgreSQL 9.0 will be installed on your system by the Makefile.install
 prerequisite installer if packages are available for your distribution, or
-you will have to compile PostgreSQL 8.3 or 8.4 from source and install it (which
-is beyond the scope of this document). PostgreSQL 8.4 is recommended if you have
-the option.
+you will have to compile PostgreSQL 9.0 from source and install it (which
+is beyond the scope of this document).
 
 Once the PostgreSQL database server has been installed, you will need to
 create the database and add the appropriate languages and extensions to
 support Evergreen. Issue the following commands as the "postgres" user to set
 up a database called "evergreen". Note that the location of the PostgreSQL
 "contrib" packages may vary depending on your distribution. In the following
-commands, we assume that you are working with PostgreSQL 8.4 on a Debian-based
+commands, we assume that you are working with PostgreSQL 9.0 on a Debian-based
 system:
 
-createdb --template template0 --encoding UNICODE evergreen
+createdb --template template0 --lc-ctype=C --lc-collate=C --encoding UNICODE evergreen
 createlang plperl evergreen
 createlang plperlu evergreen
 createlang plpgsql evergreen
-psql -f /usr/share/postgresql/8.4/contrib/tablefunc.sql -d evergreen
-psql -f /usr/share/postgresql/8.4/contrib/tsearch2.sql -d evergreen
-psql -f /usr/share/postgresql/8.4/contrib/pgxml.sql -d evergreen
+psql -f /usr/share/postgresql/9.0/contrib/tablefunc.sql -d evergreen
+psql -f /usr/share/postgresql/9.0/contrib/tsearch2.sql -d evergreen
+psql -f /usr/share/postgresql/9.0/contrib/pgxml.sql -d evergreen
+psql -f /usr/share/postgresql/9.0/contrib/hstore.sql -d evergreen
 
 Once you have created the Evergreen database, you need to create a PostgreSQL
 user to access the database. Issue the following command as the "postgres"
index 26c62f2..b72b61f 100644 (file)
@@ -312,7 +312,7 @@ install: updatepo project fmidl2fmidlent fmidlpo2entity-en
        cp $(PROJECT)/$(LOCALE)/TranslatorPopup.js $(DOJO_WIDGET_SRC)/$(DOJO_LOCALE)/TranslatorPopup.js
        cp $(PROJECT)/$(LOCALE)/XULTermLoader.js $(DOJO_WIDGET_SRC)/$(DOJO_LOCALE)/XULTermLoader.js
 
-install_all_locales: newpo newpot
+install_all_locales: newpot
        for i in `ls po/*/*po|grep -v en-US | cut -f3 -d'/' | cut -f1 -d. | sort | uniq`; \
        do \
        make LOCALE=$$i install; \