ExpiresByType application/x-javascript A64800
ExpiresByType text/css A3000
-
-
-
-# ----------------------------------------------------------------------------------
-# Set up our main virtual host
-# ----------------------------------------------------------------------------------
-NameVirtualHost *:80
-<VirtualHost *:80>
- ServerName localhost:80
- ServerAlias 127.0.0.1:80
- DocumentRoot /openils/var/web/
- DirectoryIndex index.xml index.html index.xhtml
- # - absorb the shared virtual host settings
- Include eg_vhost.conf
-</VirtualHost>
-
-
-
-
-
# ----------------------------------------------------------------------------------
# Set up our SSL virtual host
# ----------------------------------------------------------------------------------
</VirtualHost>
+# ----------------------------------------------------------------------------------
+# Set up our main virtual host
+# Port 80 comes after 443 to avoid "unknown protocol speaking not SSL to HTTPS port!?"
+# errors, per http://wiki.apache.org/httpd/InternalDummyConnection
+# ----------------------------------------------------------------------------------
+NameVirtualHost *:80
+<VirtualHost *:80>
+ ServerName localhost:80
+ ServerAlias 127.0.0.1:80
+ DocumentRoot /openils/var/web/
+ DirectoryIndex index.xml index.html index.xhtml
+ # - absorb the shared virtual host settings
+ Include eg_vhost.conf
+</VirtualHost>
allow from all
</Location>
+<Location /opac/extras/merge_template>
+ SetHandler perl-script
+ PerlSetVar OILSProxyTitle "Batch Update Login"
+ PerlSetVar OILSProxyDescription "Please log in to update records in batch"
+ PerlSetVar OILSProxyPermissions "STAFF_LOGIN"
+ PerlHandler OpenILS::WWW::Proxy OpenILS::WWW::TemplateBatchBibUpdate
+ PerlSendHeader On
+ Options +ExecCGI
+ allow from all
+</Location>
+
+<Location /opac/extras/circ>
+ SetHandler perl-script
+ PerlSetVar OILSProxyTitle "Circ Extras Login"
+ PerlSetVar OILSProxyDescription "Please log in with an authorized staff account to export records"
+ PerlSetVar OILSProxyPermissions "STAFF_LOGIN"
+ PerlHandler OpenILS::WWW::Proxy
+ Options +ExecCGI
+ PerlSendHeader On
+ allow from all
+</Location>
+
# ----------------------------------------------------------------------------------
# Reporting output lives here
# ----------------------------------------------------------------------------------
<fields oils_persist:primary="id" oils_persist:sequence="config.hold_matrix_matchpoint_id_seq">
<field reporter:label="Matchpoint ID" name="id" reporter:datatype="id"/>
<field reporter:label="Active?" name="active" reporter:datatype="bool"/>
+ <field reporter:label="Strict OU matches?" name="strict_ou_match" reporter:datatype="bool"/>
<field reporter:label="User Home Library" name="user_home_ou" reporter:datatype="org_unit"/>
<field reporter:label="Request Library" name="request_ou" reporter:datatype="org_unit"/>
<field reporter:label="Pickup Library" name="pickup_ou" reporter:datatype="org_unit"/>
<field name="name" reporter:datatype="text"/>
<field name="normal" reporter:datatype="interval"/>
<field name="shrt" reporter:datatype="interval"/>
+ <field name="date_ceiling" reporter:datatype="timestamp"/>
</fields>
<links>
</links>
</actions>
</permacrud>
</class>
+
+ <class id="chdd" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::hard_due_date" oils_persist:tablename="config.hard_due_date" reporter:label="Hard Due Date">
+ <fields oils_persist:primary="id" oils_persist:sequence="config.hard_due_date_id_seq">
+ <field reporter:label="ID" name="id" reporter:datatype="id"/>
+ <field reporter:label="Duration Rule" name="duration_rule" reporter:datatype="link"/>
+ <field reporter:label="Ceiling Date" name="ceiling_date" reporter:datatype="timestamp"/>
+ <field reporter:label="Active Date" name="active_date" reporter:datatype="timestamp"/>
+ </fields>
+ <links>
+ <link field="duration_rule" reltype="has_a" key="id" map="" class="crcd"/>
+ </links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ </permacrud>
+ </class>
+
<class id="mobts" controller="open-ils.cstore" oils_obj:fieldmapper="money::open_billable_transaction_summary" oils_persist:tablename="money.open_billable_xact_summary" reporter:label="Open Billable Transaction Summary">
<fields oils_persist:primary="id" oils_persist:sequence="">
<field name="balance_owed" reporter:datatype="money"/>
<field reporter:label="User Expiration Interval" name="perm_interval" reporter:datatype="interval"/>
<field reporter:label="Required Permission" name="application_perm" reporter:datatype="text"/>
<field reporter:label="Is User Group" name="usergroup" reporter:datatype="bool"/>
+ <field reporter:label="Hold Priority" name="hold_priority" reporter:datatype="int"/>
</fields>
<links>
<link field="parent" reltype="has_a" key="id" map="" class="pgt"/>
<field name="owner" reporter:datatype="link"/>
<field name="create_time" reporter:datatype="timestamp"/>
<field name="template" reporter:datatype="link"/>
- <field name="data" reporter:datatype="link"/>
+ <field name="data" reporter:datatype="text"/>
<field name="folder" reporter:datatype="link"/>
<field name="recur" reporter:datatype="bool"/>
<field name="recurrence" reporter:datatype="interval"/>
-->
</options>
+ <checkin_override>
+ <event>COPY_ALERT_MESSAGE</event>
+ <event>COPY_BAD_STATUS</event>
+ <event>COPY_STATUS_MISSING</event>
+ <!--
+ <event>COPY_STATUS_LOST</event>
+ -->
+ </checkin_override>
+
<!-- If uncommented, overrides the legacy_script_support value in opensrf.xml for SIP. -->
<!--
<legacy_script_support>false</legacy_script_support>
<!-- memcache servers -->
<global>
<servers>
- <server>localhost:11211</server>
+ <server>127.0.0.1:11211</server>
</servers>
<max_cache_time>86400</max_cache_time>
</global>
<anon>
<!-- anonymous cache. currently, primarily used for web session caching -->
<servers>
- <server>localhost:11211</server>
+ <server>127.0.0.1:11211</server>
</servers>
<max_cache_time>1800</max_cache_time>
<!-- maximum size of a single cache entry / default = 100k-->
<min_spare_children>1</min_spare_children>
<max_spare_children>5</max_spare_children>
</unix_config>
+ <app_settings>
+ <!-- number of parallel open-ils.trigger processes to use for collection and reaction -->
+ <!--
+ <parallel>
+ <collect>3</collect>
+ <react>3</react>
+ </parallel>
+ -->
+ </app_settings>
</open-ils.trigger>
<opensrf.math>
# This CGI script might be useful for providing an easy way for EZproxy to authenticate
# users against an Evergreen instance.
#
-# For example, if you modify your eg_vhost.conf by adding this:
+# For example, if you modify your eg.conf by adding this:
+# Alias "/cgi-bin/ezproxy/" "/openils/var/cgi-bin/ezproxy/"
# <Directory "/openils/var/cgi-bin/ezproxy">
# AddHandler cgi-script .pl
# AllowOverride None
use OpenSRF::EX qw(:try);
use OpenSRF::System;
-
my $bootstrap = '/openils/conf/opensrf_core.xml';
my $cgi = new CGI;
my $u = $cgi->param('user');
+my $usrname = $cgi->param('usrname');
+my $barcode = $cgi->param('barcode');
my $p = $cgi->param('passwd');
print $cgi->header(-type=>'text/html', -expires=>'-1d');
OpenSRF::System->bootstrap_client( config_file => $bootstrap );
-if (!$u || !$p) {
- print "+INCOMPLETE";
+if (!($u || $usrname || $barcode) || !$p) {
+ print '+INCOMPLETE';
} else {
- my $nametype = 'username';
- $nametype = 'barcode' if ($u =~ /^\d+$/o);
+ my $nametype;
+ if ($usrname) {
+ $u = $usrname;
+ $nametype = 'username';
+ } elsif ($barcode) {
+ $u = $barcode;
+ $nametype = 'barcode';
+ } else {
+ $nametype = 'username';
+ my $regex_response = OpenSRF::AppSession
+ ->create('open-ils.actor')
+ ->request('open-ils.actor.ou_setting.ancestor_default', 1, 'opac.barcode_regex')
+ ->gather(1);
+ if ($regex_response) {
+ my $regexp = $regex_response->{'value'};
+ $nametype = 'barcode' if ($u =~ qr/$regexp/);
+ }
+ }
my $seed = OpenSRF::AppSession
- ->create("open-ils.auth")
+ ->create('open-ils.auth')
->request( 'open-ils.auth.authenticate.init', $u )
->gather(1);
if ($seed) {
my $response = OpenSRF::AppSession
- ->create("open-ils.auth")
- ->request( 'open-ils.auth.authenticate.complete', { $nametype => $u, password => md5_hex($seed . md5_hex($p)), type => 'temp' })
+ ->create('open-ils.auth')
+ ->request( 'open-ils.auth.authenticate.complete', { $nametype => $u, password => md5_hex($seed . md5_hex($p)), type => 'opac' })
->gather(1);
if ($response->{payload}->{authtoken}) {
my $user = OpenSRF::AppSession
- ->create("open-ils.auth")
- ->request( "open-ils.auth.session.retrieve", $response->{payload}->{authtoken} )
+ ->create('open-ils.auth')
+ ->request( 'open-ils.auth.session.retrieve', $response->{payload}->{authtoken} )
->gather(1);
if (ref($user) eq 'HASH' && $user->{ilsevent} == 1001) {
- print "+NO";
+ print '+NO';
} else {
- print "+VALID";
+ print '+VALID';
}
} else {
- print "+NO";
+ print '+NO';
}
} else {
- print "+BACKEND_ERROR";
+ print '+BACKEND_ERROR';
}
-
}
1;
jsonObject*, const char*, osrfMethodContext* );
static char* searchPredicate ( const ClassInfo*, osrfHash*, jsonObject*, osrfMethodContext* );
static char* searchJOIN ( const jsonObject*, const ClassInfo* left_info );
-static char* searchWHERE ( const jsonObject*, const ClassInfo*, int, osrfMethodContext* );
-static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* );
+static char* searchWHERE ( const jsonObject* search_hash, const ClassInfo*, int, osrfMethodContext* );
+static char* buildSELECT( const jsonObject*, jsonObject* rest_of_query,
+ osrfHash* meta, osrfMethodContext* ctx );
+static char* buildOrderByFromArray( osrfMethodContext* ctx, const jsonObject* order_array );
+
char* buildQuery( osrfMethodContext* ctx, jsonObject* query, int flags );
char* SELECT ( osrfMethodContext*, jsonObject*, const jsonObject*, const jsonObject*,
jsonIteratorFree( selclass_itr );
}
-
char* col_list = buffer_release( select_buf );
// Make sure the SELECT list isn't empty. This can happen, for example,
}
}
- growing_buffer* order_buf = NULL; // to collect ORDER BY list
-
// Build an ORDER BY clause, if there is one
if( NULL == order_hash )
; // No ORDER BY? do nothing
else if( JSON_ARRAY == order_hash->type ) {
- // Array of field specifications, each specification being a
- // hash to define the class, field, and other details
- int order_idx = 0;
- jsonObject* order_spec;
- while( (order_spec = jsonObjectGetIndex( order_hash, order_idx++ ) ) ) {
-
- if( JSON_HASH != order_spec->type ) {
- osrfLogError( OSRF_LOG_MARK,
- "%s: Malformed field specification in ORDER BY clause; expected JSON_HASH, found %s",
- modulename, json_type( order_spec->type ) );
- if( ctx )
- osrfAppSessionStatus(
- ctx->session,
- OSRF_STATUS_INTERNALSERVERERROR,
- "osrfMethodException",
- ctx->request,
- "Malformed ORDER BY clause -- see error log for more details"
- );
- buffer_free( order_buf );
- free( having_buf );
- buffer_free( group_buf );
- buffer_free( sql_buf );
- if( defaultselhash )
- jsonObjectFree( defaultselhash );
- return NULL;
- }
-
- const char* class_alias =
- jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "class" ) );
- const char* field =
- jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "field" ) );
-
- if( order_buf )
- OSRF_BUFFER_ADD( order_buf, ", " );
- else
- order_buf = buffer_init( 128 );
-
- if( !field || !class_alias ) {
- osrfLogError( OSRF_LOG_MARK,
- "%s: Missing class or field name in field specification "
- "of ORDER BY clause",
- modulename );
- if( ctx )
- osrfAppSessionStatus(
- ctx->session,
- OSRF_STATUS_INTERNALSERVERERROR,
- "osrfMethodException",
- ctx->request,
- "Malformed ORDER BY clause -- see error log for more details"
- );
- buffer_free( order_buf );
- free( having_buf );
- buffer_free( group_buf );
- buffer_free( sql_buf );
- if( defaultselhash )
- jsonObjectFree( defaultselhash );
- return NULL;
- }
-
- ClassInfo* order_class_info = search_alias( class_alias );
- if( ! order_class_info ) {
- osrfLogError( OSRF_LOG_MARK, "%s: ORDER BY clause references class \"%s\" "
- "not in FROM clause", modulename, class_alias );
- if( ctx )
- osrfAppSessionStatus(
- ctx->session,
- OSRF_STATUS_INTERNALSERVERERROR,
- "osrfMethodException",
- ctx->request,
- "Invalid class referenced in ORDER BY clause -- "
- "see error log for more details"
- );
- free( having_buf );
- buffer_free( group_buf );
- buffer_free( sql_buf );
- if( defaultselhash )
- jsonObjectFree( defaultselhash );
- return NULL;
- }
-
- osrfHash* field_def = osrfHashGet( order_class_info->fields, field );
- if( !field_def ) {
- osrfLogError( OSRF_LOG_MARK,
- "%s: Invalid field \"%s\".%s referenced in ORDER BY clause",
- modulename, class_alias, field );
- if( ctx )
- osrfAppSessionStatus(
- ctx->session,
- OSRF_STATUS_INTERNALSERVERERROR,
- "osrfMethodException",
- ctx->request,
- "Invalid field referenced in ORDER BY clause -- "
- "see error log for more details"
- );
- free( having_buf );
- buffer_free( group_buf );
- buffer_free( sql_buf );
- if( defaultselhash )
- jsonObjectFree( defaultselhash );
- return NULL;
- } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
- osrfLogError( OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
- modulename, field );
- if( ctx )
- osrfAppSessionStatus(
- ctx->session,
- OSRF_STATUS_INTERNALSERVERERROR,
- "osrfMethodException",
- ctx->request,
- "Virtual field in ORDER BY clause -- see error log for more details"
- );
- buffer_free( order_buf );
- free( having_buf );
- buffer_free( group_buf );
- buffer_free( sql_buf );
- if( defaultselhash )
- jsonObjectFree( defaultselhash );
- return NULL;
- }
-
- if( jsonObjectGetKeyConst( order_spec, "transform" ) ) {
- char* transform_str = searchFieldTransform(
- class_alias, field_def, order_spec );
- if( ! transform_str ) {
- if( ctx )
- osrfAppSessionStatus(
- ctx->session,
- OSRF_STATUS_INTERNALSERVERERROR,
- "osrfMethodException",
- ctx->request,
- "Severe query error in ORDER BY clause -- "
- "see error log for more details"
- );
- buffer_free( order_buf );
- free( having_buf );
- buffer_free( group_buf );
- buffer_free( sql_buf );
- if( defaultselhash )
- jsonObjectFree( defaultselhash );
- return NULL;
- }
-
- OSRF_BUFFER_ADD( order_buf, transform_str );
- free( transform_str );
- }
- else
- buffer_fadd( order_buf, "\"%s\".%s", class_alias, field );
-
- const char* direction =
- jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "direction" ) );
- if( direction ) {
- if( direction[ 0 ] || 'D' == direction[ 0 ] )
- OSRF_BUFFER_ADD( order_buf, " DESC" );
- else
- OSRF_BUFFER_ADD( order_buf, " ASC" );
- }
+ order_by_list = buildOrderByFromArray( ctx, order_hash );
+ if( !order_by_list ) {
+ free( having_buf );
+ buffer_free( group_buf );
+ buffer_free( sql_buf );
+ if( defaultselhash )
+ jsonObjectFree( defaultselhash );
+ return NULL;
}
} else if( JSON_HASH == order_hash->type ) {
// This hash is keyed on class alias. Each class has either
// an array of field names or a hash keyed on field name.
+ growing_buffer* order_buf = NULL; // to collect ORDER BY list
jsonIterator* class_itr = jsonNewIterator( order_hash );
while( (snode = jsonIteratorNext( class_itr )) ) {
"osrfMethodException",
ctx->request,
"Invalid class referenced in ORDER BY clause -- "
- "see error log for more details"
+ "see error log for more details"
);
jsonIteratorFree( class_itr );
buffer_free( order_buf );
}
} // end while
jsonIteratorFree( class_itr );
+ if( order_buf )
+ order_by_list = buffer_release( order_buf );
} else {
osrfLogError( OSRF_LOG_MARK,
"%s: Malformed ORDER BY clause; expected JSON_HASH or JSON_ARRAY, found %s",
ctx->request,
"Malformed ORDER BY clause -- see error log for more details"
);
- buffer_free( order_buf );
free( having_buf );
buffer_free( group_buf );
buffer_free( sql_buf );
jsonObjectFree( defaultselhash );
return NULL;
}
-
- if( order_buf )
- order_by_list = buffer_release( order_buf );
}
-
string = buffer_release( group_buf );
if( *string && ( aggregate_found || (flags & SELECT_DISTINCT) ) ) {
} // end of SELECT()
-static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrfHash* meta, osrfMethodContext* ctx ) {
+/**
+ @brief Build a list of ORDER BY expressions.
+ @param ctx Pointer to the method context.
+ @param order_array Pointer to a JSON_ARRAY of field specifications.
+ @return Pointer to a string containing a comma-separated list of ORDER BY expressions.
+ Each expression may be either a column reference or a function call whose first parameter
+ is a column reference.
+
+ Each entry in @a order_array must be a JSON_HASH with values for "class" and "field".
+ It may optionally include entries for "direction" and/or "transform".
+
+ The calling code is responsible for freeing the returned string.
+*/
+static char* buildOrderByFromArray( osrfMethodContext* ctx, const jsonObject* order_array ) {
+ if( ! order_array ) {
+ osrfLogError( OSRF_LOG_MARK, "%s: Logic error: NULL pointer for ORDER BY clause",
+ modulename );
+ if( ctx )
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Logic error: ORDER BY clause expected, not found; "
+ "see error log for more details"
+ );
+ return NULL;
+ } else if( order_array->type != JSON_ARRAY ) {
+ osrfLogError( OSRF_LOG_MARK,
+ "%s: Logic error: Expected JSON_ARRAY for ORDER BY clause, not found", modulename );
+ if( ctx )
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Logic error: Unexpected format for ORDER BY clause; see error log for more details" );
+ return NULL;
+ }
+
+ growing_buffer* order_buf = buffer_init( 128 );
+ int first = 1; // boolean
+ int order_idx = 0;
+ jsonObject* order_spec;
+ while( (order_spec = jsonObjectGetIndex( order_array, order_idx++ ))) {
+
+ if( JSON_HASH != order_spec->type ) {
+ osrfLogError( OSRF_LOG_MARK,
+ "%s: Malformed field specification in ORDER BY clause; "
+ "expected JSON_HASH, found %s",
+ modulename, json_type( order_spec->type ) );
+ if( ctx )
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Malformed ORDER BY clause -- see error log for more details"
+ );
+ buffer_free( order_buf );
+ return NULL;
+ }
+
+ const char* class_alias =
+ jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "class" ));
+ const char* field =
+ jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "field" ));
+
+ if( !field || !class_alias ) {
+ osrfLogError( OSRF_LOG_MARK,
+ "%s: Missing class or field name in field specification of ORDER BY clause",
+ modulename );
+ if( ctx )
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Malformed ORDER BY clause -- see error log for more details"
+ );
+ buffer_free( order_buf );
+ return NULL;
+ }
+
+ const ClassInfo* order_class_info = search_alias( class_alias );
+ if( ! order_class_info ) {
+ osrfLogInternal( OSRF_LOG_MARK, "%s: ORDER BY clause references class \"%s\" "
+ "not in FROM clause, skipping it", modulename, class_alias );
+ continue;
+ }
+
+ // Add a separating comma, except at the beginning
+ if( first )
+ first = 0;
+ else
+ OSRF_BUFFER_ADD( order_buf, ", " );
+
+ osrfHash* field_def = osrfHashGet( order_class_info->fields, field );
+ if( !field_def ) {
+ osrfLogError( OSRF_LOG_MARK,
+ "%s: Invalid field \"%s\".%s referenced in ORDER BY clause",
+ modulename, class_alias, field );
+ if( ctx )
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Invalid field referenced in ORDER BY clause -- "
+ "see error log for more details"
+ );
+ free( order_buf );
+ return NULL;
+ } else if( str_is_true( osrfHashGet( field_def, "virtual" ) ) ) {
+ osrfLogError( OSRF_LOG_MARK, "%s: Virtual field \"%s\" in ORDER BY clause",
+ modulename, field );
+ if( ctx )
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Virtual field in ORDER BY clause -- see error log for more details"
+ );
+ buffer_free( order_buf );
+ return NULL;
+ }
+
+ if( jsonObjectGetKeyConst( order_spec, "transform" )) {
+ char* transform_str = searchFieldTransform( class_alias, field_def, order_spec );
+ if( ! transform_str ) {
+ if( ctx )
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Severe query error in ORDER BY clause -- "
+ "see error log for more details"
+ );
+ buffer_free( order_buf );
+ return NULL;
+ }
+
+ OSRF_BUFFER_ADD( order_buf, transform_str );
+ free( transform_str );
+ }
+ else
+ buffer_fadd( order_buf, "\"%s\".%s", class_alias, field );
+
+ const char* direction =
+ jsonObjectGetString( jsonObjectGetKeyConst( order_spec, "direction" ) );
+ if( direction ) {
+ if( direction[ 0 ] || 'D' == direction[ 0 ] )
+ OSRF_BUFFER_ADD( order_buf, " DESC" );
+ else
+ OSRF_BUFFER_ADD( order_buf, " ASC" );
+ }
+ }
+
+ return buffer_release( order_buf );
+}
+
+/**
+ @brief Build a SELECT statement.
+ @param search_hash Pointer to a JSON_HASH or JSON_ARRAY encoding the WHERE clause.
+ @param rest_of_query Pointer to a JSON_HASH containing any other SQL clauses.
+ @param meta Pointer to the class metadata for the core class.
+ @param ctx Pointer to the method context.
+ @return Pointer to a character string containing the WHERE clause; or NULL upon error.
+
+ Within the rest_of_query hash, the meaningful keys are "join", "select", "no_i18n",
+ "order_by", "limit", and "offset".
+
+ The SELECT statements built here are distinct from those built for the json_query method.
+*/
+static char* buildSELECT ( const jsonObject* search_hash, jsonObject* rest_of_query,
+ osrfHash* meta, osrfMethodContext* ctx ) {
const char* locale = osrf_message_get_last_locale();
osrfHash* fields = osrfHashGet( meta, "fields" );
- char* core_class = osrfHashGet( meta, "classname" );
+ const char* core_class = osrfHashGet( meta, "classname" );
- const jsonObject* join_hash = jsonObjectGetKeyConst( order_hash, "join" );
+ const jsonObject* join_hash = jsonObjectGetKeyConst( rest_of_query, "join" );
- jsonObject* node = NULL;
- jsonObject* snode = NULL;
- jsonObject* onode = NULL;
- const jsonObject* _tmp = NULL;
jsonObject* selhash = NULL;
jsonObject* defaultselhash = NULL;
growing_buffer* sql_buf = buffer_init( 128 );
growing_buffer* select_buf = buffer_init( 128 );
- if( !(selhash = jsonObjectGetKey( order_hash, "select" )) ) {
+ if( !(selhash = jsonObjectGetKey( rest_of_query, "select" )) ) {
defaultselhash = jsonNewObjectType( JSON_HASH );
selhash = defaultselhash;
}
jsonObjectSetKey( selhash, core_class, field_list );
}
+ // Build a list of columns for the SELECT clause
int first = 1;
+ const jsonObject* snode = NULL;
jsonIterator* class_itr = jsonNewIterator( selhash );
- while( (snode = jsonIteratorNext( class_itr )) ) {
+ while( (snode = jsonIteratorNext( class_itr )) ) { // For each class
+ // If the class isn't in the IDL, ignore it
const char* cname = class_itr->key;
osrfHash* idlClass = osrfHashGet( oilsIDL(), cname );
if( !idlClass )
continue;
- if( strcmp(core_class,class_itr->key )) {
+ // If the class isn't the core class, and isn't in the JOIN clause, ignore it
+ if( strcmp( core_class, class_itr->key )) {
if( !join_hash )
continue;
- jsonObject* found = jsonObjectFindPath( join_hash, "//%s", class_itr->key );
+ jsonObject* found = jsonObjectFindPath( join_hash, "//%s", class_itr->key );
if( !found->size ) {
jsonObjectFree( found );
continue;
jsonObjectFree( found );
}
+ const jsonObject* node = NULL;
jsonIterator* select_itr = jsonNewIterator( snode );
while( (node = jsonIteratorNext( select_itr )) ) {
const char* item_str = jsonObjectGetString( node );
if( locale ) {
const char* i18n;
- const jsonObject* no_i18n_obj = jsonObjectGetKeyConst( order_hash, "no_i18n" );
+ const jsonObject* no_i18n_obj = jsonObjectGetKeyConst( rest_of_query, "no_i18n" );
if( obj_is_true( no_i18n_obj ) ) // Suppress internationalization?
i18n = NULL;
else
ctx->request,
"Unable to build query frame for core class"
);
+ buffer_free( sql_buf );
+ if( defaultselhash )
+ jsonObjectFree( defaultselhash );
return NULL;
}
+ // Add the JOIN clauses, if any
if( join_hash ) {
char* join_clause = searchJOIN( join_hash, &curr_query->core );
OSRF_BUFFER_ADD_CHAR( sql_buf, ' ' );
}
osrfLogDebug( OSRF_LOG_MARK, "%s pre-predicate SQL = %s",
- modulename, OSRF_BUFFER_C_STR( sql_buf ));
+ modulename, OSRF_BUFFER_C_STR( sql_buf ));
OSRF_BUFFER_ADD( sql_buf, " WHERE " );
+ // Add the conditions in the WHERE clause
char* pred = searchWHERE( search_hash, &curr_query->core, AND_OP_JOIN, ctx );
if( !pred ) {
osrfAppSessionStatus(
free( pred );
}
- if( order_hash ) {
- char* string = NULL;
- if( (_tmp = jsonObjectGetKeyConst( order_hash, "order_by" )) ){
-
- growing_buffer* order_buf = buffer_init( 128 );
-
- first = 1;
- jsonIterator* class_itr = jsonNewIterator( _tmp );
- while( (snode = jsonIteratorNext( class_itr )) ) {
-
- if( !jsonObjectGetKeyConst( selhash,class_itr->key ))
- continue;
-
- if( snode->type == JSON_HASH ) {
+ // Add the ORDER BY, LIMIT, and/or OFFSET clauses, if present
+ if( rest_of_query ) {
+ const jsonObject* order_by = NULL;
+ if( ( order_by = jsonObjectGetKeyConst( rest_of_query, "order_by" )) ){
- jsonIterator* order_itr = jsonNewIterator( snode );
- while( (onode = jsonIteratorNext( order_itr )) ) {
+ char* order_by_list = NULL;
- osrfHash* field_def = oilsIDLFindPath( "/%s/fields/%s",
- class_itr->key, order_itr->key );
- if( !field_def )
- continue;
+ if( JSON_ARRAY == order_by->type ) {
+ order_by_list = buildOrderByFromArray( ctx, order_by );
+ if( !order_by_list ) {
+ buffer_free( sql_buf );
+ if( defaultselhash )
+ jsonObjectFree( defaultselhash );
+ clear_query_stack();
+ return NULL;
+ }
+ } else if( JSON_HASH == order_by->type ) {
+ // We expect order_by to be a JSON_HASH keyed on class names. Traverse it
+ // and build a list of ORDER BY expressions.
+ growing_buffer* order_buf = buffer_init( 128 );
+ first = 1;
+ jsonIterator* class_itr = jsonNewIterator( order_by );
+ while( (snode = jsonIteratorNext( class_itr )) ) { // For each class:
+
+ ClassInfo* order_class_info = search_alias( class_itr->key );
+ if( ! order_class_info )
+ continue; // class not referenced by FROM clause? Ignore it.
+
+ if( JSON_HASH == snode->type ) {
+
+ // If the data for the current class is a JSON_HASH, then it is
+ // keyed on field name.
+
+ const jsonObject* onode = NULL;
+ jsonIterator* order_itr = jsonNewIterator( snode );
+ while( (onode = jsonIteratorNext( order_itr )) ) { // For each field
+
+ osrfHash* field_def = osrfHashGet(
+ order_class_info->fields, order_itr->key );
+ if( !field_def )
+ continue; // Field not defined in IDL? Ignore it.
+ if( str_is_true( osrfHashGet( field_def, "virtual")))
+ continue; // Field is virtual? Ignore it.
+
+ char* field_str = NULL;
+ char* direction = NULL;
+ if( onode->type == JSON_HASH ) {
+ if( jsonObjectGetKeyConst( onode, "transform" ) ) {
+ field_str = searchFieldTransform(
+ class_itr->key, field_def, onode );
+ if( ! field_str ) {
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Severe query error in ORDER BY clause -- "
+ "see error log for more details"
+ );
+ jsonIteratorFree( order_itr );
+ jsonIteratorFree( class_itr );
+ buffer_free( order_buf );
+ buffer_free( sql_buf );
+ if( defaultselhash )
+ jsonObjectFree( defaultselhash );
+ clear_query_stack();
+ return NULL;
+ }
+ } else {
+ growing_buffer* field_buf = buffer_init( 16 );
+ buffer_fadd( field_buf, "\"%s\".%s",
+ class_itr->key, order_itr->key );
+ field_str = buffer_release( field_buf );
+ }
- char* direction = NULL;
- if( onode->type == JSON_HASH ) {
- if( jsonObjectGetKeyConst( onode, "transform" ) ) {
- string = searchFieldTransform( class_itr->key, field_def, onode );
- if( ! string ) {
- osrfAppSessionStatus(
- ctx->session,
- OSRF_STATUS_INTERNALSERVERERROR,
- "osrfMethodException",
- ctx->request,
- "Severe query error in ORDER BY clause -- "
- "see error log for more details"
- );
- jsonIteratorFree( order_itr );
- jsonIteratorFree( class_itr );
- buffer_free( order_buf );
- buffer_free( sql_buf );
- if( defaultselhash )
- jsonObjectFree( defaultselhash );
- clear_query_stack();
- return NULL;
+ if( ( order_by = jsonObjectGetKeyConst( onode, "direction" )) ) {
+ const char* dir = jsonObjectGetString( order_by );
+ if(!strncasecmp( dir, "d", 1 )) {
+ direction = " DESC";
+ }
}
} else {
- growing_buffer* field_buf = buffer_init( 16 );
- buffer_fadd( field_buf, "\"%s\".%s",
- class_itr->key, order_itr->key );
- string = buffer_release( field_buf );
- }
-
- if( (_tmp = jsonObjectGetKeyConst( onode, "direction" )) ) {
- const char* dir = jsonObjectGetString( _tmp );
- if(!strncasecmp( dir, "d", 1 )) {
+ field_str = strdup( order_itr->key );
+ const char* dir = jsonObjectGetString( onode );
+ if( !strncasecmp( dir, "d", 1 )) {
direction = " DESC";
+ } else {
+ direction = " ASC";
}
}
- } else {
- string = strdup( order_itr->key );
- const char* dir = jsonObjectGetString( onode );
- if( !strncasecmp( dir, "d", 1 )) {
- direction = " DESC";
+
+ if( first ) {
+ first = 0;
} else {
- direction = " ASC";
+ buffer_add( order_buf, ", " );
}
- }
- if( first ) {
- first = 0;
- } else {
- buffer_add( order_buf, ", " );
- }
-
- buffer_add( order_buf, string );
- free( string );
+ buffer_add( order_buf, field_str );
+ free( field_str );
- if( direction ) {
- buffer_add( order_buf, direction );
+ if( direction ) {
+ buffer_add( order_buf, direction );
+ }
+ } // end while; looping over ORDER BY expressions
+
+ jsonIteratorFree( order_itr );
+
+ } else if( JSON_STRING == snode->type ) {
+ // We expect a comma-separated list of sort fields.
+ const char* str = jsonObjectGetString( snode );
+ if( strchr( str, ';' )) {
+ // No semicolons allowed. It is theoretically possible for a
+ // legitimate semicolon to occur within quotes, but it's not likely
+ // to occur in practice in the context of an ORDER BY list.
+ osrfLogError( OSRF_LOG_MARK, "%s: Possible attempt at SOL injection -- "
+ "semicolon found in ORDER BY list: \"%s\"", modulename, str );
+ if( ctx ) {
+ osrfAppSessionStatus(
+ ctx->session,
+ OSRF_STATUS_INTERNALSERVERERROR,
+ "osrfMethodException",
+ ctx->request,
+ "Possible attempt at SOL injection -- "
+ "semicolon found in ORDER BY list"
+ );
+ }
+ jsonIteratorFree( class_itr );
+ buffer_free( order_buf );
+ buffer_free( sql_buf );
+ if( defaultselhash )
+ jsonObjectFree( defaultselhash );
+ clear_query_stack();
+ return NULL;
}
+ buffer_add( order_buf, str );
+ break;
}
- jsonIteratorFree( order_itr );
+ } // end while; looping over order_by classes
- } else {
- const char* str = jsonObjectGetString( snode );
- buffer_add( order_buf, str );
- break;
- }
+ jsonIteratorFree( class_itr );
+ order_by_list = buffer_release( order_buf );
+ } else {
+ osrfLogWarning( OSRF_LOG_MARK,
+ "\"order_by\" object in a query is not a JSON_HASH or JSON_ARRAY;"
+ "no ORDER BY generated" );
}
- jsonIteratorFree( class_itr );
-
- string = buffer_release( order_buf );
-
- if( *string ) {
+ if( order_by_list && *order_by_list ) {
OSRF_BUFFER_ADD( sql_buf, " ORDER BY " );
- OSRF_BUFFER_ADD( sql_buf, string );
+ OSRF_BUFFER_ADD( sql_buf, order_by_list );
}
- free( string );
+ free( order_by_list );
}
- if( (_tmp = jsonObjectGetKeyConst( order_hash, "limit" )) ) {
- const char* str = jsonObjectGetString( _tmp );
+ const jsonObject* limit = jsonObjectGetKeyConst( rest_of_query, "limit" );
+ if( limit ) {
+ const char* str = jsonObjectGetString( limit );
buffer_fadd(
sql_buf,
" LIMIT %d",
);
}
- _tmp = jsonObjectGetKeyConst( order_hash, "offset" );
- if( _tmp ) {
- const char* str = jsonObjectGetString( _tmp );
+ const jsonObject* offset = jsonObjectGetKeyConst( rest_of_query, "offset" );
+ if( offset ) {
+ const char* str = jsonObjectGetString( offset );
buffer_fadd(
sql_buf,
" OFFSET %d",
--- /dev/null
+#!/bin/bash
+#
+# JEDI converter scripts installation
+#
+# RHEL/CENTOS Install
+# note: need older version of rubygems since RHEL package for ruby is old
+#
+# run this script as root or with sudo
+
+yum install ruby ruby-devel ruby-rdoc
+
+wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.5.tgz
+tar zxvf rubygems-1.3.5.tgz
+pushd rubygems-1.3.5
+ruby setup.rb # this gives harmless errors about README files missing
+gem install rubygems-update
+update_rubygems
+popd
+
+# RHEL has a bug in json module, but mbklein moved to using yajl for us....
+gem install parseconfig rspec edi4r edi4r-tdid rcov openils-mapper # mkmf
+
__PACKAGE__->register_method(
- method => 'create_lineitem',
- api_name => 'open-ils.acq.lineitem.create',
- signature => {
- desc => 'Creates a lineitem',
+ method => 'create_lineitem',
+ api_name => 'open-ils.acq.lineitem.create',
+ signature => {
+ desc => 'Creates a lineitem',
params => [
{desc => 'Authentication token', type => 'string'},
{desc => 'The lineitem object to create', type => 'object'},
__PACKAGE__->register_method(
- method => 'retrieve_lineitem',
- api_name => 'open-ils.acq.lineitem.retrieve',
- signature => {
- desc => 'Retrieves a lineitem',
+ method => 'retrieve_lineitem',
+ api_name => 'open-ils.acq.lineitem.retrieve',
+ signature => {
+ desc => 'Retrieves a lineitem',
params => [
{desc => 'Authentication token', type => 'string'},
{desc => 'lineitem ID to retrieve', type => 'number'},
}
}
- return $e->event unless (
+ return $e->event unless ((
$li->purchase_order and
($no_auth or $e->allowed(['VIEW_PURCHASE_ORDER', 'CREATE_PURCHASE_ORDER'],
$li->purchase_order->ordering_agency, $li->purchase_order))
- ) or (
+ ) or (
$li->picklist and !$li->purchase_order and # user doesn't have view_po perms
($no_auth or $e->allowed(['VIEW_PICKLIST', 'CREATE_PICKLIST'],
$li->picklist->org_unit, $li->picklist))
- );
+ ));
unless ($$options{flesh_po}) {
$li->purchase_order(
__PACKAGE__->register_method(
- method => 'delete_lineitem',
- api_name => 'open-ils.acq.lineitem.delete',
- signature => {
- desc => 'Deletes a lineitem',
+ method => 'delete_lineitem',
+ api_name => 'open-ils.acq.lineitem.delete',
+ signature => {
+ desc => 'Deletes a lineitem',
params => [
- {desc => 'Authentication token', type => 'string'},
+ {desc => 'Authentication token', type => 'string'},
{desc => 'lineitem ID to delete', type => 'number'},
],
return => {desc => '1 on success, Event on error'}
__PACKAGE__->register_method(
- method => 'update_lineitem',
- api_name => 'open-ils.acq.lineitem.update',
- signature => {
- desc => 'Update one or many lineitems',
+ method => 'update_lineitem',
+ api_name => 'open-ils.acq.lineitem.update',
+ signature => {
+ desc => 'Update one or many lineitems',
params => [
- {desc => 'Authentication token', type => 'string'},
+ {desc => 'Authentication token', type => 'string'},
{desc => 'lineitem object update', type => 'object'}
],
return => {desc => '1 on success, Event on error'}
__PACKAGE__->register_method(
- method => 'lineitem_detail_CUD_batch_api',
- api_name => 'open-ils.acq.lineitem_detail.cud.batch',
- stream => 1,
- signature => {
- desc => q/Creates a new purchase order line item detail.
- Additionally creates the associated fund_debit/,
+ method => 'lineitem_detail_CUD_batch_api',
+ api_name => 'open-ils.acq.lineitem_detail.cud.batch',
+ stream => 1,
+ signature => {
+ desc => q/Creates a new purchase order line item detail. / .
+ q/Additionally creates the associated fund_debit/,
params => [
{desc => 'Authentication token', type => 'string'},
{desc => 'List of lineitem_details to create', type => 'array'},
);
__PACKAGE__->register_method(
- method => 'lineitem_detail_CUD_batch_api',
- api_name => 'open-ils.acq.lineitem_detail.cud.batch.dry_run',
- stream => 1,
+ method => 'lineitem_detail_CUD_batch_api',
+ api_name => 'open-ils.acq.lineitem_detail.cud.batch.dry_run',
+ stream => 1,
signature => {
desc => q/
Dry run version of open-ils.acq.lineitem_detail.cud.batch.
__PACKAGE__->register_method(
- method => 'receive_po_api',
- api_name => 'open-ils.acq.purchase_order.receive'
+ method => 'receive_po_api',
+ api_name => 'open-ils.acq.purchase_order.receive'
);
sub receive_po_api {
}
__PACKAGE__->register_method(
- method => 'rollback_receive_lineitem_api',
- api_name => 'open-ils.acq.lineitem.receive.rollback',
- signature => {
- desc => 'Mark a lineitem as Un-received',
+ method => 'rollback_receive_lineitem_api',
+ api_name => 'open-ils.acq.lineitem.receive.rollback',
+ signature => {
+ desc => 'Mark a lineitem as Un-received',
params => [
{desc => 'Authentication token', type => 'string'},
- {desc => 'lineitem ID', type => 'number'}
+ {desc => 'lineitem ID', type => 'number'}
],
return => {desc =>
"on success, object describing changes to LI and possibly PO; " .
__PACKAGE__->register_method(
- method => 'set_lineitem_price_api',
- api_name => 'open-ils.acq.lineitem.price.set',
- signature => {
- desc => 'Set lineitem price. If debits already exist, update them as well',
+ method => 'set_lineitem_price_api',
+ api_name => 'open-ils.acq.lineitem.price.set',
+ signature => {
+ desc => 'Set lineitem price. If debits already exist, update them as well',
params => [
{desc => 'Authentication token', type => 'string'},
- {desc => 'lineitem ID', type => 'number'}
+ {desc => 'lineitem ID', type => 'number'}
],
return => {desc => 'status blob, Event on error'}
}
__PACKAGE__->register_method(
- method => 'clone_picklist_api',
- api_name => 'open-ils.acq.picklist.clone',
- signature => {
- desc => 'Clones a picklist, including lineitem and lineitem details',
+ method => 'clone_picklist_api',
+ api_name => 'open-ils.acq.picklist.clone',
+ signature => {
+ desc => 'Clones a picklist, including lineitem and lineitem details',
params => [
{desc => 'Authentication token', type => 'string'},
{desc => 'Picklist ID', type => 'number'},
__PACKAGE__->register_method(
- method => 'merge_picklist_api',
- api_name => 'open-ils.acq.picklist.merge',
- signature => {
- desc => 'Merges 2 or more picklists into a single list',
+ method => 'merge_picklist_api',
+ api_name => 'open-ils.acq.picklist.merge',
+ signature => {
+ desc => 'Merges 2 or more picklists into a single list',
params => [
{desc => 'Authentication token', type => 'string'},
{desc => 'Lead Picklist ID', type => 'number'},
__PACKAGE__->register_method(
- method => 'delete_picklist_api',
- api_name => 'open-ils.acq.picklist.delete',
- signature => {
- desc => q/Deletes a picklist. It also deletes any lineitems in the "new" state.
- Other attached lineitems are detached'/,
+ method => 'delete_picklist_api',
+ api_name => 'open-ils.acq.picklist.delete',
+ signature => {
+ desc => q/Deletes a picklist. It also deletes any lineitems in the "new" state. / .
+ q/Other attached lineitems are detached/,
params => [
- {desc => 'Authentication token', type => 'string'},
+ {desc => 'Authentication token', type => 'string'},
{desc => 'Picklist ID to delete', type => 'number'}
],
return => {desc => '1 on success, Event on error'}
__PACKAGE__->register_method(
- method => 'activate_purchase_order',
- api_name => 'open-ils.acq.purchase_order.activate.dry_run'
+ method => 'activate_purchase_order',
+ api_name => 'open-ils.acq.purchase_order.activate.dry_run'
);
__PACKAGE__->register_method(
- method => 'activate_purchase_order',
- api_name => 'open-ils.acq.purchase_order.activate',
- signature => {
- desc => q/Activates a purchase order. This updates the status of the PO
- and Lineitems to 'on-order'. Activated PO's are ready for EDI delivery
- if appropriate./,
+ method => 'activate_purchase_order',
+ api_name => 'open-ils.acq.purchase_order.activate',
+ signature => {
+ desc => q/Activates a purchase order. This updates the status of the PO / .
+ q/and Lineitems to 'on-order'. Activated PO's are ready for EDI delivery if appropriate./,
params => [
{desc => 'Authentication token', type => 'string'},
{desc => 'Purchase ID', type => 'number'}
}
# tell the world we activated a PO
- $U->create_events_for_hook('acqpo.activated', $po, $po->ordering_agency);
+ $U->create_events_for_hook('acqpo.activated', $po, $po->ordering_agency) unless $dry_run;
return undef;
}
__PACKAGE__->register_method(
- method => 'split_purchase_order_by_lineitems',
- api_name => 'open-ils.acq.purchase_order.split_by_lineitems',
- signature => {
- desc => q/Splits a PO into many POs, 1 per lineitem. Only works for
- POs a) with more than one lineitems, and b) in the "pending" state./,
+ method => 'split_purchase_order_by_lineitems',
+ api_name => 'open-ils.acq.purchase_order.split_by_lineitems',
+ signature => {
+ desc => q/Splits a PO into many POs, 1 per lineitem. Only works for / .
+ q/POs a) with more than one lineitems, and b) in the "pending" state./,
params => [
{desc => 'Authentication token', type => 'string'},
- {desc => 'Purchase order ID', type => 'number'}
+ {desc => 'Purchase order ID', type => 'number'}
],
return => {desc => 'list of new PO IDs on success, Event on error'}
}
__PACKAGE__->register_method(
method => "user_settings",
+ authoritative => 1,
api_name => "open-ils.actor.patron.settings.retrieve",
);
sub user_settings {
my $api = $self->api_name;
if( $api =~ /password/o ) {
-
# make sure the original password matches the in-database password
- return OpenILS::Event->new('INCORRECT_PASSWORD')
- if md5_hex($orig_pw) ne $db_user->passwd;
+ if (md5_hex($orig_pw) ne $db_user->passwd) {
+ $e->rollback;
+ return new OpenILS::Event('INCORRECT_PASSWORD');
+ }
$db_user->passwd($new_val);
} else {
# make sure no one else has this username
my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
- return OpenILS::Event->new('USERNAME_EXISTS') if @$exist;
+ if (@$exist) {
+ $e->rollback;
+ return new OpenILS::Event('USERNAME_EXISTS');
+ }
$db_user->usrname($new_val);
} elsif( $api =~ /email/o ) {
sub update_user_note {
my( $self, $conn, $auth, $note ) = @_;
my $e = new_editor(authtoken=>$auth, xact=>1);
- return $e->event unless $e->checkauth;
+ return $e->die_event unless $e->checkauth;
my $patron = $e->retrieve_actor_user($note->usr)
- or return $e->event;
- return $e->event unless
+ or return $e->die_event;
+ return $e->die_event unless
$e->allowed('UPDATE_USER', $patron->home_ou);
$e->update_actor_user_note($note)
- or return $e->event;
+ or return $e->die_event;
$e->commit;
return 1;
}
"flesh_fields" => { "au" => $fields }
}
]
- ) or return $e->event;
+ ) or return $e->die_event;
if( grep { $_ eq 'addresses' } @$fields ) {
sub apply_friend_perms {
my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
my $e = new_editor(authtoken => $auth, xact => 1);
- return $e->event unless $e->checkauth;
+ return $e->die_event unless $e->checkauth;
if($user_id != $e->requestor->id) {
my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
sub update_user_pending_address {
my($self, $conn, $auth, $addr) = @_;
my $e = new_editor(authtoken => $auth, xact => 1);
- return $e->event unless $e->checkauth;
+ return $e->die_event unless $e->checkauth;
if($addr->usr != $e->requestor->id) {
my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
}
+sub patientreq {
+ my ($self, $client, $service, $method, @params) = @_;
+ my ($response, $err);
+
+ my $session = create OpenSRF::AppSession($service);
+ my $request = $session->request($method, @params);
+
+ my $spurt = 10;
+ my $give_up = time + 1000;
+
+ try {
+ while (time < $give_up) {
+ $response = $request->recv("timeout" => $spurt);
+ last if $request->complete;
+
+ $client->status(new OpenSRF::DomainObject::oilsContinueStatus);
+ }
+ } catch Error with {
+ $err = shift;
+ };
+
+ if ($err) {
+ warn "received error : service=$service : method=$method : params=".Dumper(\@params) . "\n $err";
+ throw $err ("Call to $service for method $method \n failed with exception: $err : " );
+ }
+
+ return $response->content;
+}
+
# This logic now lives in storage
sub __patron_money_owed {
my( $self, $patronid ) = @_;
# most appropriate event. create the event, fire it, then return the resulting
# event with fleshed template_output and error_output
sub fire_object_event {
- my($self, $event_def, $hook, $object, $context_org, $granularity, $user_data) = @_;
+ my($self, $event_def, $hook, $object, $context_org, $granularity, $user_data, $client) = @_;
my $e = OpenILS::Utils::CStoreEditor->new;
my $def;
or return $e->event;
}
+ my $final_resp;
+
if($def->group_field) {
# we have a list of objects
$object = [$object] unless ref $object eq 'ARRAY';
$logger->info("EVENTS = " . OpenSRF::Utils::JSON->perl2JSON(\@event_ids));
- my $resp = $self->simplereq(
- 'open-ils.trigger',
- 'open-ils.trigger.event_group.fire',
- \@event_ids);
+ my $resp;
+ if (not defined $client) {
+ $resp = $self->simplereq(
+ 'open-ils.trigger',
+ 'open-ils.trigger.event_group.fire',
+ \@event_ids);
+ } else {
+ $resp = $self->patientreq(
+ $client,
+ "open-ils.trigger", "open-ils.trigger.event_group.fire",
+ \@event_ids
+ );
+ }
- return undef unless $resp and $resp->{events} and @{$resp->{events}};
+ if($resp and $resp->{events} and @{$resp->{events}}) {
- return $e->retrieve_action_trigger_event([
- $resp->{events}->[0]->id,
- {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
- ]);
+ $e->xact_begin;
+ $final_resp = $e->retrieve_action_trigger_event([
+ $resp->{events}->[0]->id,
+ {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
+ ]);
+ $e->rollback;
+ }
} else {
$object = $$object[0] if ref $object eq 'ARRAY';
- my $event_id = $self->simplereq(
- 'open-ils.trigger', $auto_method, $def->id, $object, $context_org, $user_data);
-
- my $resp = $self->simplereq(
- 'open-ils.trigger',
- 'open-ils.trigger.event.fire',
- $event_id);
-
- return undef unless $resp and $resp->{event};
-
- return $e->retrieve_action_trigger_event([
- $resp->{event}->id,
- {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
- ]);
+ my $event_id;
+ my $resp;
+
+ if (not defined $client) {
+ $event_id = $self->simplereq(
+ 'open-ils.trigger',
+ $auto_method, $def->id, $object, $context_org, $user_data
+ );
+
+ $resp = $self->simplereq(
+ 'open-ils.trigger',
+ 'open-ils.trigger.event.fire',
+ $event_id
+ );
+ } else {
+ $event_id = $self->patientreq(
+ $client,
+ 'open-ils.trigger',
+ $auto_method, $def->id, $object, $context_org, $user_data
+ );
+
+ $resp = $self->patientreq(
+ $client,
+ 'open-ils.trigger',
+ 'open-ils.trigger.event.fire',
+ $event_id
+ );
+ }
+
+ if($resp and $resp->{event}) {
+ $e->xact_begin;
+ $final_resp = $e->retrieve_action_trigger_event([
+ $resp->{event}->id,
+ {flesh => 1, flesh_fields => {atev => ['template_output', 'error_output']}}
+ ]);
+ $e->rollback;
+ }
}
+
+ return $final_resp;
}
}) or return $e->die_event;
if (@$rows < 1) {
+ $e->rollback;
return $rows;
} else {
# More than one result might be possible, but we don't want to return
# more than one at this time.
my $id = $rows->[0]->{"id"};
- return $e->retrieve_booking_reservation([
+ my $resp =$e->retrieve_booking_reservation([
$id, {
"flesh" => 2,
"flesh_fields" => {
}
}
]) or $e->die_event;
+ $e->rollback;
+ return $resp;
}
}
}
__PACKAGE__->register_method(
+ method => "template_overlay_container",
+ api_name => "open-ils.cat.container.template_overlay",
+ stream => 1,
+ signature => q#
+ Overlays biblio.record_entry MARC values
+ @param auth The authtoken
+ @param container The container, um, containing the records to be updated by the template
+ @param template The overlay template, or nothing and the method will look for a negative bib id in the container
+ @return Stream of hashes record id in the key "record" and t or f for the success of the overlay operation in key "success"
+ #
+);
+
+__PACKAGE__->register_method(
+ method => "template_overlay_container",
+ api_name => "open-ils.cat.container.template_overlay.background",
+ stream => 1,
+ signature => q#
+ Overlays biblio.record_entry MARC values
+ @param auth The authtoken
+ @param container The container, um, containing the records to be updated by the template
+ @param template The overlay template, or nothing and the method will look for a negative bib id in the container
+ @return Cache key to check for status of the container overlay
+ #
+);
+
+sub template_overlay_container {
+ my($self, $conn, $auth, $container, $template) = @_;
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+
+ my $actor = OpenSRF::AppSession->create('open-ils.actor') if ($self->api_name =~ /background$/);
+
+ my $items = $e->search_container_biblio_record_entry_bucket_item({ bucket => $container });
+
+ my $titem;
+ if (!$template) {
+ ($titem) = grep { $_->target_biblio_record_entry < 0 } @$items;
+ if (!$titem) {
+ $e->rollback;
+ return undef;
+ }
+ $items = [grep { $_->target_biblio_record_entry > 0 } @$items];
+
+ $template = $e->retrieve_biblio_record_entry( $titem->target_biblio_record_entry )->marc;
+ }
+
+ my $responses = [];
+ my $some_failed = 0;
+
+ $self->respond_complete(
+ $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses)->gather(1)
+ ) if ($actor);
+
+ for my $item ( @$items ) {
+ my $rec = $e->retrieve_biblio_record_entry($item->target_biblio_record_entry);
+ next unless $rec;
+
+ my $success = 'f';
+ if ($e->allowed('UPDATE_RECORD', $rec->owner, $rec)) {
+ $success = $e->json_query(
+ { from => [ 'vandelay.template_overlay_bib_record', $template, $rec->id ] }
+ )->[0]->{'vandelay.template_overlay_bib_record'};
+ }
+
+ $some_failed++ if ($success eq 'f');
+
+ if ($actor) {
+ push @$responses, { record => $rec->id, success => $success };
+ $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ } else {
+ $conn->respond({ record => $rec->id, success => $success });
+ }
+
+ if ($success eq 't') {
+ unless ($e->delete_container_biblio_record_entry_bucket_item($item)) {
+ $e->rollback;
+ if ($actor) {
+ push @$responses, { complete => 1, success => 'f' };
+ $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ return undef;
+ } else {
+ return { complete => 1, success => 'f' };
+ }
+ }
+ }
+ }
+
+ if ($titem && !$some_failed) {
+ return $e->die_event unless ($e->delete_container_biblio_record_entry_bucket_item($titem));
+ }
+
+ if ($e->commit) {
+ if ($actor) {
+ push @$responses, { complete => 1, success => 't' };
+ $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ } else {
+ return { complete => 1, success => 't' };
+ }
+ } else {
+ if ($actor) {
+ push @$responses, { complete => 1, success => 'f' };
+ $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ } else {
+ return { complete => 1, success => 'f' };
+ }
+ }
+ return undef;
+}
+
+__PACKAGE__->register_method(
method => "update_biblio_record_entry",
api_name => "open-ils.cat.biblio.record_entry.update",
signature => q/
sub fire_circ_events {
my($self, $conn, $auth, $org_id, $event_def, $hook, $granularity, $target_ids, $user_data) = @_;
- my $e = new_editor(authtoken => $auth);
+ my $e = new_editor(authtoken => $auth, xact => 1);
return $e->event unless $e->checkauth;
my $targets;
return $e->event unless $e->allowed('VIEW_CIRCULATIONS', $org_id);
$targets = $e->batch_retrieve_action_circulation($target_ids);
}
+ $e->rollback; # FIXME using transaction because of pgpool/slony setups, but not
+ # simply making this method authoritative because of weirdness
+ # with transaction handling in A/T code that causes rollback
+ # failure down the line if handling many targets
return undef unless @$targets;
return $U->fire_object_event($event_def, $hook, $targets, $org_id, $granularity, $user_data);
max_fine_rule => $max_fine_rule->name,
max_fine => $self->get_max_fine_amount($max_fine_rule),
fine_interval => $recurring_fine_rule->recurrence_interval,
- renewal_remaining => $duration_rule->max_renewals
+ renewal_remaining => $duration_rule->max_renewals,
+ duration_date_ceiling => $duration_rule->date_ceiling
};
$policy->{duration} = $duration_rule->shrt
my $recurring = $self->recurring_fines_rule;
my $copy = $self->copy;
my $patron = $self->patron;
+ my $duration_date_ceiling;
if( $duration ) {
my $policy = $self->get_circ_policy($duration, $recurring, $max);
+ $duration_date_ceiling = $policy->{duration_date_ceiling};
my $dname = $duration->name;
my $mname = $max->name;
# if a patron is renewing, 'requestor' will be the patron
$circ->circ_staff($self->editor->requestor->id);
- $circ->due_date( $self->create_due_date($circ->duration) ) if $circ->duration;
+ $circ->due_date( $self->create_due_date($circ->duration, $duration_date_ceiling) ) if $circ->duration;
$self->circ($circ);
}
sub create_due_date {
- my( $self, $duration ) = @_;
+ my( $self, $duration, $date_ceiling ) = @_;
# if there is a raw time component (e.g. from postgres),
# turn it into an interval that interval_to_seconds can parse
# add the circ duration
$due_date->add(seconds => OpenSRF::Utils->interval_to_seconds($duration));
+ if($date_ceiling) {
+ my $cdate = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($date_ceiling));
+ if ($cdate > DateTime->now and $cdate < $due_date) {
+ $logger->info("circulator: overriding due date with date ceiling: $date_ceiling");
+ $due_date = $cdate;
+ }
+ }
+
# return ISO8601 time with timezone
return $due_date->strftime('%FT%T%z');
}
$self->update_copy;
}
}
- $logger->info("LFW XXX: way down here"); # LFW XXX
if($self->claims_never_checked_out and
$U->ou_ancestor_setting_value($self->circ->circ_lib, 'circ.claim_never_checked_out.mark_missing')) {
return;
}
- $logger->warn("circulator: * hold notify failed for hold $holdid");
+ $logger->debug("circulator: * hold notify cancelled or failed for hold $holdid");
} else {
$logger->info("circulator: Not sending hold notification since the patron has no email address");
return $cl;
}
+__PACKAGE__->register_method(
+ api_name => "open-ils.circ.copy_location_order.update",
+ method => 'update_clo',
+ argc => 2,
+);
+
+sub update_clo {
+ my($self, $client, $auth, $orders) = @_;
+ return [] unless $orders and @$orders;
+
+ my $e = new_editor(authtoken => $auth, xact =>1);
+ return $e->die_event unless $e->checkauth;
+
+ my $org = $$orders[0]->org;
+ return $e->die_event unless $e->allowed('ADMIN_COPY_LOCATION_ORDER', $org);
+ # clear out the previous order entries
+ my $existing = $e->search_asset_copy_location_order({org => $org});
+ $e->delete_asset_copy_location_order($_) or return $e->die_event for @$existing;
+
+ # create the new order entries
+ my $progress = 0;
+ for my $order (@$orders) {
+ return $e->die_event(OpenILS::Event->new('BAD_PARAMS')) unless $order->org == $org;
+ $e->create_asset_copy_location_order($order) or return $e->die_event;
+ $client->respond({maximum => scalar(@$orders), progress => $progress}) unless ($progress++ % 10);
+ }
+
+ # fetch the new entries
+ $orders = $e->search_asset_copy_location_order({org => $org});
+ $e->commit;
+ return {orders => $orders};
+}
-23;
+1;
use DateTime;
use DateTime::Format::ISO8601;
use OpenSRF::Utils qw/:datetime/;
+use Digest::MD5 qw(md5_hex);
+use OpenSRF::Utils::Cache;
my $apputils = "OpenILS::Application::AppUtils";
my $U = $apputils;
sub create_hold {
my( $self, $conn, $auth, $hold ) = @_;
+ return -1 unless $hold;
my $e = new_editor(authtoken=>$auth, xact=>1);
- return $e->event unless $e->checkauth;
+ return $e->die_event unless $e->checkauth;
- return -1 unless $hold;
my $override = 1 if $self->api_name =~ /override/;
my @events;
if( $requestor->id ne $hold->usr ) {
# Make sure the requestor is allowed to place holds for
# the recipient if they are not the same people
- $recipient = $e->retrieve_actor_user($hold->usr) or return $e->event;
- $e->allowed('REQUEST_HOLDS', $recipient->home_ou) or return $e->event;
+ $recipient = $e->retrieve_actor_user($hold->usr) or return $e->die_event;
+ $e->allowed('REQUEST_HOLDS', $recipient->home_ou) or return $e->die_event;
}
# If the related org setting tells us to, block if patron privs have expired
push( @events, OpenILS::Event->new('HOLD_ITEM_CHECKED_OUT')) if $checked_out;
if ( $t eq OILS_HOLD_TYPE_METARECORD ) {
- return $e->event unless $e->allowed('MR_HOLDS', $porg);
+ return $e->die_event unless $e->allowed('MR_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_TITLE ) {
- return $e->event unless $e->allowed('TITLE_HOLDS', $porg);
+ return $e->die_event unless $e->allowed('TITLE_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_VOLUME ) {
- return $e->event unless $e->allowed('VOLUME_HOLDS', $porg);
+ return $e->die_event unless $e->allowed('VOLUME_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_ISSUANCE ) {
- return $e->event unless $e->allowed('ISSUANCE_HOLDS', $porg);
+ return $e->die_event unless $e->allowed('ISSUANCE_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_COPY ) {
- return $e->event unless $e->allowed('COPY_HOLDS', $porg);
+ return $e->die_event unless $e->allowed('COPY_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_FORCE ) {
- return $e->event unless $e->allowed('COPY_HOLDS', $porg);
+ return $e->die_event unless $e->allowed('COPY_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_RECALL ) {
- return $e->event unless $e->allowed('COPY_HOLDS', $porg);
+ return $e->die_event unless $e->allowed('COPY_HOLDS', $porg);
}
if( @events ) {
- $override or return \@events;
+ if (!$override) {
+ $e->rollback;
+ return \@events;
+ }
for my $evt (@events) {
next unless $evt;
my $name = $evt->{textcode};
- return $e->event unless $e->allowed("$name.override", $porg);
+ return $e->die_event unless $e->allowed("$name.override", $porg);
}
}
$hold->requestor($e->requestor->id);
$hold->request_lib($e->requestor->ws_ou);
$hold->selection_ou($hold->pickup_lib) unless $hold->selection_ou;
- $hold = $e->create_action_hold_request($hold) or return $e->event;
+ $hold = $e->create_action_hold_request($hold) or return $e->die_event;
$e->commit;
sub uncancel_hold {
my($self, $client, $auth, $hold_id) = @_;
my $e = new_editor(authtoken=>$auth, xact=>1);
- return $e->event unless $e->checkauth;
+ return $e->die_event unless $e->checkauth;
my $hold = $e->retrieve_action_hold_request($hold_id)
or return $e->die_event;
return $e->die_event unless $e->allowed('CANCEL_HOLDS', $hold->request_lib);
- return 0 if $hold->fulfillment_time;
- return 1 unless $hold->cancel_time;
+ if ($hold->fulfillment_time) {
+ $e->rollback;
+ return 0;
+ }
+ unless ($hold->cancel_time) {
+ $e->rollback;
+ return 1;
+ }
# if configured to reset the request time, also reset the expire time
if($U->ou_ancestor_setting_value(
my($self, $client, $auth, $holdid, $cause, $note) = @_;
my $e = new_editor(authtoken=>$auth, xact=>1);
- return $e->event unless $e->checkauth;
+ return $e->die_event unless $e->checkauth;
my $hold = $e->retrieve_action_hold_request($holdid)
- or return $e->event;
+ or return $e->die_event;
if( $e->requestor->id ne $hold->usr ) {
- return $e->event unless $e->allowed('CANCEL_HOLDS');
+ return $e->die_event unless $e->allowed('CANCEL_HOLDS');
}
- return 1 if $hold->cancel_time;
+ if ($hold->cancel_time) {
+ $e->rollback;
+ return 1;
+ }
# If the hold is captured, reset the copy status
if( $hold->capture_time and $hold->current_copy ) {
my $copy = $e->retrieve_asset_copy($hold->current_copy)
- or return $e->event;
+ or return $e->die_event;
if( $copy->status == OILS_COPY_STATUS_ON_HOLDS_SHELF ) {
$logger->info("canceling hold $holdid whose item is on the holds shelf");
$hold->cancel_cause($cause);
$hold->cancel_note($note);
$e->update_action_hold_request($hold)
- or return $e->event;
+ or return $e->die_event;
delete_hold_copy_maps($self, $e, $hold->id);
$e->commit;
+
+ $U->create_events_for_hook('hold_request.cancel.staff', $hold, $hold->pickup_lib)
+ if $e->requestor->id != $hold->usr;
+
return 1;
}
my $e = new_editor(authtoken=>$auth, xact=>1);
return $e->die_event unless $e->checkauth;
my $resp = update_hold_impl($self, $e, $hold, $values);
- return $resp if $U->event_code($resp);
+ if ($U->event_code($resp)) {
+ $e->rollback;
+ return $resp;
+ }
$e->commit; # FIXME: update_hold_impl already does $e->commit ??
return $resp;
}
# fetch cut_in_line and request_time since they're in the order_by
# and we're asking for distinct values
select => {ahr => ['id', 'cut_in_line', 'request_time']},
- from => {
- ahr => {
- ahcm => {type => 'left'} # there may be no copy maps
- }
- },
+ from => { ahr => 'ahcm' },
order_by => [
{
"class" => "ahr",
{ "class" => "ahr", "field" => "request_time" }
],
distinct => 1,
- where => {
- '-or' => [
+ where => {
+ '+ahcm' => {
+ target_copy => {
+ in => {
+ select => {ahcm => ['target_copy']},
+ from => 'ahcm',
+ where => {hold => $hold->id}
+ }
+ }
+ }
+ }
+ });
+
+ if (!@$q_holds) { # none? maybe we don't have a map ...
+ $q_holds = $e->json_query({
+ select => {ahr => ['id', 'cut_in_line', 'request_time']},
+ from => 'ahr',
+ order_by => [
{
- '+ahcm' => {
- target_copy => {
- in => {
- select => {ahcm => ['target_copy']},
- from => 'ahcm',
- where => {hold => $hold->id}
- }
- }
- }
+ "class" => "ahr",
+ "field" => "cut_in_line",
+ "transform" => "coalesce",
+ "params" => [ 0 ],
+ "direction" => "desc"
},
- {
- '+ahr' => {
- hold_type => $hold->hold_type,
- target => $hold->target
- }
- }
- ]
- },
- });
+ { "class" => "ahr", "field" => "request_time" }
+ ],
+ where => {
+ hold_type => $hold->hold_type,
+ target => $hold->target
+ }
+ });
+ }
+
my $qpos = 1;
for my $h (@$q_holds) {
sub print_hold_pull_list {
my($self, $client, $auth, $org_id) = @_;
- my $e = new_editor(authtoken=>$auth, xact=>1);
- return $e->die_event unless $e->checkauth;
+ my $e = new_editor(authtoken=>$auth);
+ return $e->event unless $e->checkauth;
$org_id = (defined $org_id) ? $org_id : $e->requestor->ws_ou;
- return $e->die_event unless $e->allowed('VIEW_HOLD', $org_id);
+ return $e->event unless $e->allowed('VIEW_HOLD', $org_id);
my $hold_ids = $U->storagereq(
'open-ils.storage.direct.action.hold_request.pull_list.id_list.current_copy_circ_lib.status_filtered.atomic',
$org_id, 10000);
return undef unless @$hold_ids;
+
$client->status(new OpenSRF::DomainObject::oilsContinueStatus);
+ # Holds will /NOT/ be in order after this ...
my $holds = $e->search_action_hold_request({id => $hold_ids}, {substream => 1});
$client->status(new OpenSRF::DomainObject::oilsContinueStatus);
- return $U->fire_object_event(undef, 'ahr.format.pull_list', $holds, $org_id);
+ # ... so we must resort.
+ my $hold_map = +{map { $_->id => $_ } @$holds};
+ my $sorted_holds = [];
+ push @$sorted_holds, $hold_map->{$_} foreach @$hold_ids;
+
+ return $U->fire_object_event(
+ undef, "ahr.format.pull_list", $sorted_holds,
+ $org_id, undef, undef, $client
+ );
+
+}
+
+__PACKAGE__->register_method(
+ method => "print_hold_pull_list_stream",
+ stream => 1,
+ api_name => "open-ils.circ.hold_pull_list.print.stream",
+ signature => {
+ desc => 'Returns a stream of fleshed holds',
+ params => [
+ { desc => 'Authtoken', type => 'string'},
+ { desc => 'Hash of optional param: Org unit ID (defaults to workstation org unit), limit, offset, sort (array of: acplo.position, call_number, request_time)',
+ type => 'object'
+ },
+ ],
+ return => {
+ desc => 'A stream of fleshed holds',
+ type => 'object'
+ }
+ }
+);
+
+sub print_hold_pull_list_stream {
+ my($self, $client, $auth, $params) = @_;
+
+ my $e = new_editor(authtoken=>$auth);
+ return $e->die_event unless $e->checkauth;
+
+ delete($$params{org_id}) unless (int($$params{org_id}));
+ delete($$params{limit}) unless (int($$params{limit}));
+ delete($$params{offset}) unless (int($$params{offset}));
+ delete($$params{chunk_size}) unless (int($$params{chunk_size}));
+ delete($$params{chunk_size}) if ($$params{chunk_size} && $$params{chunk_size} > 50); # keep the size reasonable
+ $$params{chunk_size} ||= 10;
+
+ $$params{org_id} = (defined $$params{org_id}) ? $$params{org_id}: $e->requestor->ws_ou;
+ return $e->die_event unless $e->allowed('VIEW_HOLD', $$params{org_id });
+
+ my $sort = [];
+ if ($$params{sort} && @{ $$params{sort} }) {
+ for my $s (@{ $$params{sort} }) {
+ if ($s eq 'acplo.position') {
+ push @$sort, {
+ "class" => "acplo", "field" => "position",
+ "transform" => "coalesce", "params" => [999]
+ };
+ } elsif ($s eq 'call_number') {
+ push @$sort, {"class" => "acn", "field" => "label"};
+ } elsif ($s eq 'request_time') {
+ push @$sort, {"class" => "ahr", "field" => "request_time"};
+ }
+ }
+ } else {
+ push @$sort, {"class" => "ahr", "field" => "request_time"};
+ }
+
+ my $holds_ids = $e->json_query(
+ {
+ "select" => {"ahr" => ["id"]},
+ "from" => {
+ "ahr" => {
+ "acp" => {
+ "field" => "id",
+ "fkey" => "current_copy",
+ "filter" => {
+ "circ_lib" => $$params{org_id}, "status" => [0,7]
+ },
+ "join" => {
+ "acn" => {
+ "field" => "id",
+ "fkey" => "call_number"
+ },
+ "acplo" => {
+ "field" => "org",
+ "fkey" => "circ_lib",
+ "type" => "left",
+ "filter" => {
+ "location" => {"=" => {"+acp" => "location"}}
+ }
+ }
+ }
+ }
+ }
+ },
+ "where" => {
+ "+ahr" => {
+ "capture_time" => undef,
+ "cancel_time" => undef,
+ "-or" => [
+ {"expire_time" => undef },
+ {"expire_time" => {">" => "now"}}
+ ]
+ }
+ },
+ (@$sort ? (order_by => $sort) : ()),
+ ($$params{limit} ? (limit => $$params{limit}) : ()),
+ ($$params{offset} ? (offset => $$params{offset}) : ())
+ }, {"substream" => 1}
+ ) or return $e->die_event;
+
+ $logger->info("about to stream back " . scalar(@$holds_ids) . " holds");
+
+ my @chunk;
+ for my $hid (@$holds_ids) {
+ push @chunk, $e->retrieve_action_hold_request([
+ $hid->{"id"}, {
+ "flesh" => 3,
+ "flesh_fields" => {
+ "ahr" => ["usr", "current_copy"],
+ "au" => ["card"],
+ "acp" => ["location", "call_number"],
+ "acn" => ["record"]
+ }
+ }
+ ]);
+
+ if (@chunk >= $$params{chunk_size}) {
+ $client->respond( \@chunk );
+ @chunk = ();
+ }
+ }
+ $client->respond_complete( \@chunk ) if (@chunk);
+ $e->disconnect;
+ return undef;
}
return $e->die_event unless
$e->allowed('CREATE_HOLD_NOTIFICATION', $patron->home_ou);
- $note->notify_staff($e->requestor->id);
+ $note->notify_staff($e->requestor->id);
$e->create_action_hold_notification($note) or return $e->die_event;
$e->commit;
return $note->id;
if( $hold->capture_time and $hold->current_copy ) {
my $copy = $e->retrieve_asset_copy($hold->current_copy)
- or return $e->event;
+ or return $e->die_event;
if( $copy->status == OILS_COPY_STATUS_ON_HOLDS_SHELF ) {
$logger->info("setting copy to status 'reshelving' on hold retarget");
$copy->status(OILS_COPY_STATUS_RESHELVING);
$copy->editor($e->requestor->id);
$copy->edit_date('now');
- $e->update_asset_copy($copy) or return $e->event;
+ $e->update_asset_copy($copy) or return $e->die_event;
} elsif( $copy->status == OILS_COPY_STATUS_IN_TRANSIT ) {
$logger->info("Aborting transit [$transid] on hold [$hid] reset...");
my $evt = OpenILS::Application::Circ::Transit::__abort_transit($e, $trans, $copy, 1);
$logger->info("Transit abort completed with result $evt");
- return $evt unless "$evt" eq 1;
+ unless ("$evt" eq 1) {
+ $e->rollback;
+ return $evt;
+ }
}
}
}
$hold->clear_shelf_time;
$hold->clear_shelf_expire_time;
- $e->update_action_hold_request($hold) or return $e->event;
+ $e->update_action_hold_request($hold) or return $e->die_event;
$e->commit;
$U->storagereq(
my( $self, $conn, $auth, $org ) = @_;
my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
- return $e->event unless $e->allowed('VIEW_HOLD'); # XXX rely on editor perm
+ return $e->die_event unless $e->checkauth;
+ return $e->die_event unless $e->allowed('VIEW_HOLD'); # XXX rely on editor perm
$org ||= $e->requestor->ws_ou;
}
__PACKAGE__->register_method(
+ method => "print_expired_holds_stream",
+ api_name => "open-ils.circ.captured_holds.expired.print.stream",
+ stream => 1
+);
+
+sub print_expired_holds_stream {
+ my ($self, $client, $auth, $params) = @_;
+
+ # No need to check specific permissions: we're going to call another method
+ # that will do that.
+ my $e = new_editor("authtoken" => $auth);
+ return $e->die_event unless $e->checkauth;
+
+ delete($$params{org_id}) unless (int($$params{org_id}));
+ delete($$params{limit}) unless (int($$params{limit}));
+ delete($$params{offset}) unless (int($$params{offset}));
+ delete($$params{chunk_size}) unless (int($$params{chunk_size}));
+ delete($$params{chunk_size}) if ($$params{chunk_size} && $$params{chunk_size} > 50); # keep the size reasonable
+ $$params{chunk_size} ||= 10;
+
+ $$params{org_id} = (defined $$params{org_id}) ? $$params{org_id}: $e->requestor->ws_ou;
+
+ my @hold_ids = $self->method_lookup(
+ "open-ils.circ.captured_holds.id_list.expired_on_shelf.retrieve"
+ )->run($auth, $params->{"org_id"});
+
+ if (!@hold_ids) {
+ $e->disconnect;
+ return;
+ } elsif (defined $U->event_code($hold_ids[0])) {
+ $e->disconnect;
+ return $hold_ids[0];
+ }
+
+ $logger->info("about to stream back up to " . scalar(@hold_ids) . " expired holds");
+
+ while (@hold_ids) {
+ my @hid_chunk = splice @hold_ids, 0, $params->{"chunk_size"};
+
+ my $result_chunk = $e->json_query({
+ "select" => {
+ "acp" => ["barcode"],
+ "au" => [qw/
+ first_given_name second_given_name family_name alias
+ /],
+ "acn" => ["label"],
+ "bre" => ["marc"],
+ "acpl" => ["name"]
+ },
+ "from" => {
+ "ahr" => {
+ "acp" => {
+ "field" => "id", "fkey" => "current_copy",
+ "join" => {
+ "acn" => {
+ "field" => "id", "fkey" => "call_number",
+ "join" => {
+ "bre" => {
+ "field" => "id", "fkey" => "record"
+ }
+ }
+ },
+ "acpl" => {"field" => "id", "fkey" => "location"}
+ }
+ },
+ "au" => {"field" => "id", "fkey" => "usr"}
+ }
+ },
+ "where" => {"+ahr" => {"id" => \@hid_chunk}}
+ }) or return $e->die_event;
+ $client->respond($result_chunk);
+ }
+
+ $e->disconnect;
+ undef;
+}
+
+__PACKAGE__->register_method(
method => "check_title_hold_batch",
api_name => "open-ils.circ.title_hold.is_possible.batch",
stream => 1,
copy => $copy,
pickup_lib => $hold->pickup_lib,
request_lib => $rlib,
+ retarget => 1
}
);
patron_first => $user->first_given_name,
patron_last => $user->family_name,
patron_barcode => $card->barcode,
+ patron_alias => $user->alias,
%$details
};
}
return ( $U->record_to_mvr($title), $volume, $copy, $issuance );
}
+__PACKAGE__->register_method(
+ method => 'clear_shelf_cache',
+ api_name => 'open-ils.circ.hold.clear_shelf.get_cache',
+ stream => 1,
+ signature => {
+ desc => q/
+ Returns the holds processed with the given cache key
+ /
+ }
+);
+
+sub clear_shelf_cache {
+ my($self, $client, $auth, $cache_key, $chunk_size) = @_;
+ my $e = new_editor(authtoken => $auth, xact => 1);
+ return $e->die_event unless $e->checkauth and $e->allowed('VIEW_HOLD');
+
+ $chunk_size ||= 25;
+ my $hold_data = OpenSRF::Utils::Cache->new('global')->get_cache($cache_key);
+
+ if (!$hold_data) {
+ $logger->info("no hold data found in cache"); # XXX TODO return event
+ $e->rollback;
+ return undef;
+ }
+
+ my $maximum = 0;
+ foreach (keys %$hold_data) {
+ $maximum += scalar(@{ $hold_data->{$_} });
+ }
+ $client->respond({"maximum" => $maximum, "progress" => 0});
+
+ for my $action (sort keys %$hold_data) {
+ while (@{$hold_data->{$action}}) {
+ my @hid_chunk = splice @{$hold_data->{$action}}, 0, $chunk_size;
+
+ my $result_chunk = $e->json_query({
+ "select" => {
+ "acp" => ["barcode"],
+ "au" => [qw/
+ first_given_name second_given_name family_name alias
+ /],
+ "acn" => ["label"],
+ "bre" => ["marc"],
+ "acpl" => ["name"],
+ "ahr" => ["id"]
+ },
+ "from" => {
+ "ahr" => {
+ "acp" => {
+ "field" => "id", "fkey" => "current_copy",
+ "join" => {
+ "acn" => {
+ "field" => "id", "fkey" => "call_number",
+ "join" => {
+ "bre" => {
+ "field" => "id", "fkey" => "record"
+ }
+ }
+ },
+ "acpl" => {"field" => "id", "fkey" => "location"}
+ }
+ },
+ "au" => {"field" => "id", "fkey" => "usr"}
+ }
+ },
+ "where" => {"+ahr" => {"id" => \@hid_chunk}}
+ }, {"substream" => 1}) or return $e->die_event;
+
+ $client->respond([
+ map {
+ +{"action" => $action, "hold_details" => $_}
+ } @$result_chunk
+ ]);
+ }
+ }
+
+ $e->rollback;
+ return undef;
+}
+
__PACKAGE__->register_method(
method => 'clear_shelf_process',
my $e = new_editor(authtoken=>$auth, xact => 1);
$e->checkauth or return $e->die_event;
+ my $cache = OpenSRF::Utils::Cache->new('global');
$org_id ||= $e->requestor->ws_ou;
$e->allowed('UPDATE_HOLD', $org_id) or return $e->die_event;
pickup_lib => $org_id,
cancel_time => undef,
fulfillment_time => undef,
- shelf_time => {'!=' => undef}
+ shelf_time => {'!=' => undef},
+ capture_time => {'!=' => undef},
+ current_copy => {'!=' => undef},
},
{ idlist => 1 }
);
-
my @holds;
+ my $chunk_size = 25; # chunked status updates
+ my $counter = 0;
for my $hold_id (@$hold_ids) {
$logger->info("Clear shelf processing hold $hold_id");
}
push(@holds, $hold);
+ $client->respond({maximum => scalar(@holds), progress => $counter}) if ( (++$counter % $chunk_size) == 0);
}
if ($e->commit) {
+ my %cache_data = (
+ hold => [],
+ transit => [],
+ shelf => []
+ );
+
for my $hold (@holds) {
my $copy = $hold->current_copy;
-
my ($alt_hold) = __PACKAGE__->find_nearest_permitted_hold($e, $copy, $e->requestor, 1);
if($alt_hold) {
- # copy is needed for a hold
- $client->respond({action => 'hold', copy => $copy, hold_id => $hold->id});
+ push(@{$cache_data{hold}}, $hold->id); # copy is needed for a hold
} elsif($copy->circ_lib != $e->requestor->ws_ou) {
- # copy needs to transit
- $client->respond({action => 'transit', copy => $copy, hold_id => $hold->id});
+ push(@{$cache_data{transit}}, $hold->id); # copy needs to transit
} else {
- # copy needs to go back to the shelf
- $client->respond({action => 'shelf', copy => $copy, hold_id => $hold->id});
+ push(@{$cache_data{shelf}}, $hold->id); # copy needs to go back to the shelf
}
}
- # tell the client we're done
- $client->respond_complete;
-
- # fire off the hold cancelation trigger
- my $trigger = OpenSRF::AppSession->connect('open-ils.trigger');
-
- for my $hold (@holds) {
-
- my $req = $trigger->request(
- 'open-ils.trigger.event.autocreate',
- 'hold_request.cancel.expire_holds_shelf',
- $hold, $org_id);
+ my $cache_key = md5_hex(time . $$ . rand());
+ $logger->info("clear_shelf_cache: storing under $cache_key");
+ $cache->put_cache($cache_key, \%cache_data, 7200); # TODO: 2 hours. configurable?
- # wait for response so don't flood the service
- $req->recv;
- }
+ # tell the client we're done
+ $client->respond_complete({cache_key => $cache_key});
- $trigger->disconnect;
+ # fire off the hold cancelation trigger and wait for response so don't flood the service
+ $U->create_events_for_hook(
+ 'hold_request.cancel.expire_holds_shelf',
+ $_, $org_id, undef, undef, 1) for @holds;
} else {
# tell the client we're done
my( $self, $client, $auth, $new_bib_id, $bib_ids ) = @_;
my $e = new_editor(authtoken=>$auth, xact=>1);
- return $e->event unless $e->checkauth;
+ return $e->die_event unless $e->checkauth;
my $holds = $e->search_action_hold_request(
[
my $search_duration;
my $user_offset = $search_hash->{offset} || 0; # user-specified offset
my $user_limit = $search_hash->{limit} || 10;
+ my $ignore_facet_classes = $search_hash->{ignore_facet_classes};
$user_offset = ($user_offset >= 0) ? $user_offset : 0;
$user_limit = ($user_limit >= 0) ? $user_limit : 10;
}
);
- cache_facets($facet_key, $new_ids, $IAmMetabib) if $docache;
+ cache_facets($facet_key, $new_ids, $IAmMetabib, $ignore_facet_classes) if $docache;
return undef;
}
sub cache_facets {
# add facets for this search to the facet cache
- my($key, $results, $metabib) = @_;
+ my($key, $results, $metabib, $ignore) = @_;
my $data = $cache->get_cache($key);
$data ||= {};
+ if (!ref($ignore)) {
+ $ignore = ['identifier']; # ignore the identifier class by default
+ }
+
return undef unless (@$results);
# The query we're constructing
},
from => {
mfae => {
- mmrsm => { field => 'source', fkey => 'source' }
+ mmrsm => { field => 'source', fkey => 'source' },
+ cmf => { field => 'id', fkey => 'field' }
}
},
where => {
- '+mmrsm' => { $count_field => $results }
+ '+mmrsm' => { $count_field => $results },
+ '+cmf' => { field_class => { 'not in' => $ignore } }
}
}
);
__PACKAGE__->table( 'asset_copy_location' );
__PACKAGE__->columns( Primary => qw/id/ );
-__PACKAGE__->columns( Essential => qw/name owning_lib holdable hold_verify opac_visible circulate/ );
+__PACKAGE__->columns( Essential => qw/name owning_lib holdable hold_verify opac_visible circulate label_prefix label_suffix/ );
#-------------------------------------------------------------------------------
package asset::copy_location_order;
__PACKAGE__->table( 'asset_call_number' );
__PACKAGE__->columns( Primary => qw/id/ );
__PACKAGE__->columns( Essential => qw/record label creator create_date editor
- edit_date record label owning_lib deleted/ );
+ edit_date record label owning_lib deleted label_class label_sortkey/ );
#-------------------------------------------------------------------------------
package asset::call_number_note;
fine_level circulate deposit price ref opac_visible
circ_as_type circ_modifier deposit_amount location mint_condition
holdable dummy_title dummy_author deleted alert_message
- age_protect floating/ );
+ age_protect floating cost status_changed_time/ );
#-------------------------------------------------------------------------------
package asset::stat_cat;
__PACKAGE__->table('permission_grp_tree');
__PACKAGE__->columns(Primary => qw/id/);
__PACKAGE__->columns(Essential => qw/name parent description perm_interval
- application_perm usergroup/);
+ application_perm usergroup hold_priority/);
#-------------------------------------------------------------------------------
package permission::usr_grp_map;
use base qw/permission/;
holdable dummy_title dummy_author deleted alert_message label
age_protect floating label_sort_key contents/ );
+#-------------------------------------------------------------------------------
+package serial::record_entry;
+use base qw/serial/;
+
+__PACKAGE__->table( 'serial_record_entry' );
+__PACKAGE__->columns( Primary => qw/id/ );
+__PACKAGE__->columns( Essential => qw/active record create_date creator
+ deleted edit_date editor id last_xact_id marc source
+ owning_lib/ );
+
+
1;
}
if (($filters{preferred_language} || $self->QueryParser->default_preferred_language) && ($filters{preferred_language_multiplier} || $self->QueryParser->default_preferred_language_multiplier)) {
- $rel = "($rel * CASE WHEN FIRST(mrd.item_lang) = ". $self->QueryParser->quote_value( $filters{preferred_language} ? $filters{preferred_language} : $self->QueryParser->default_preferred_language ) . " THEN ";
- $rel .= $filters{preferred_language_multiplier} ? $filters{preferred_language_multiplier} : $self->QueryParser->default_preferred_language_multiplier;
- $rel .= " ELSE 1 END)";
+ 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 .= "::NUMERIC";
+ $rel .= '::NUMERIC';
for my $f ( qw/audience vr_format item_type item_form lit_form language bib_level/ ) {
my $col = $f;
$desc = 'DESC' if ($self->find_modifier('descending'));
if ($sort_filter eq 'rel') { # relevance ranking flips sort dir
- if ($desc eq 'ASC') {
+ if ($desc eq 'ASC') {
$desc = 'DESC';
} else {
$desc = 'ASC';
}
} else {
if ($sort_filter eq 'title') {
- my $default = $desc eq 'DESC' ? ' ' : 'zzzzzz';
- $rank = <<" SQL";
-( COALESCE( FIRST ((
- SELECT frt.value
- FROM metabib.full_rec frt
- WHERE frt.record = m.source
- AND frt.tag = 'tnf'
- AND frt.subfield = 'a'
- LIMIT 1
- )),'$default'))::TEXT
- SQL
+ $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') {
- my $default = $desc eq 'DESC' ? '0' : '99999';
- $rank = "COALESCE( FIRST(NULLIF(LPAD(REGEXP_REPLACE(mrd.date1, E'\\\\D+', '0', 'g'),4,'0'),'0000')), '$default' )::INT";
+ $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)) )::TIMESTAMPTZ";
+ $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)) )::TIMESTAMPTZ";
+ $rank = "FIRST((SELECT edit_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))";
} elsif ($sort_filter eq 'author') {
- my $default = $desc eq 'DESC' ? ' ' : 'zzzzzz';
- $rank = <<" SQL"
-( COALESCE( FIRST ((
- SELECT LTRIM(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::text::int
- LIMIT 1
- )),'$default'))::TEXT
- SQL
+ $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;
ARRAY_ACCUM(DISTINCT m.source) AS records,
$rel AS rel,
$rank AS rank,
- COALESCE( FIRST(NULLIF(LPAD(REGEXP_REPLACE(mrd.date1, E'\\\\D+', '0', 'g'),4,'0'),'0000')), '0' )::INT AS tie_break
+ FIRST(mrd.date1) AS tie_break
FROM metabib.metarecord_source_map m
JOIN metabib.rec_descriptor mrd ON (m.source = mrd.record)
$$flat_plan{from}
$bib_level
AND $$flat_plan{where}
GROUP BY 1
- ORDER BY 4 $desc, 5 DESC, 3 DESC
+ ORDER BY 4 $desc NULLS LAST, 5 DESC NULLS LAST, 3 DESC
LIMIT $core_limit
SQL
return '' if (!@$only_atoms);
if ($bump eq 'first_word') {
- return " /* first_word */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ ('^'||naco_normalize(".$self->QueryParser->quote_value($only_atoms->[0]->content).")))::BOOL::INT, 0 ) * $multiplier, 1)";
+ return " /* first_word */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ ('^'||naco_normalize(".$self->QueryParser->quote_value($only_atoms->[0]->content)."))), FALSE )::INT * $multiplier, 1)";
} elsif ($bump eq 'full_match') {
return " /* full_match */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ ('^'||".
- join( "||' '||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms )."||'\$'))::BOOL::INT, 0 ) * $multiplier, 1)";
+ join( "||' '||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms )."||'\$')), FALSE )::INT * $multiplier, 1)";
} elsif ($bump eq 'word_order') {
return " /* word_order */ COALESCE(NULLIF( (naco_normalize(".$node->table_alias.".value) ~ (".
- join( "||'.*'||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms )."))::BOOL::INT, 0 ) * $multiplier, 1)";
+ join( "||'.*'||", map { "naco_normalize(".$self->QueryParser->quote_value($_->content).")" } @$only_atoms ).")), FALSE )::INT * $multiplier, 1)";
}
return '';
my $node_rank = $node->rank . " * ${talias}.weight";
my $core_limit = $self->QueryParser->core_limit || 25000;
- $from .= "\n\tLEFT JOIN (\n\t\tSELECT fe.*, fe_weight.weight /* search */\n\t\t FROM $table AS fe";
+ $from .= "\n\tLEFT JOIN (\n\t\tSELECT fe.*, fe_weight.weight, x.tsq /* search */\n\t\t FROM $table AS fe";
$from .= "\n\t\t\tJOIN config.metabib_field AS fe_weight ON (fe_weight.id = fe.field)";
- $from .= "\n\t\t WHERE fe.index_vector @@ (" .$node->tsquery . ')';
+ $from .= "\n\t\t\tJOIN (SELECT ".$node->tsquery ." AS tsq ) AS x ON (fe.index_vector @@ x.tsq)";
my @bump_fields;
if (@{$node->fields} > 0) {
@bump_fields = @{$node->fields};
- $from .= "\n\t\t\tAND fe_weight.field_class = ". $self->QueryParser->quote_value($node->classname) ." AND fe_weight.name IN (";
- $from .= join(",", map { $self->QueryParser->quote_value($_) } @{$node->fields}) . ")";
+
+ my @field_ids;
+ push(@field_ids, $self->QueryParser->search_field_ids_by_class( $node->classname, $_ )->[0]) for (@bump_fields);
+ $from .= "\n\t\t\tWHERE fe_weight.id IN (". join(',', @field_ids) .")";
} else {
@bump_fields = @{$self->QueryParser->search_fields->{$node->classname}};
}
- $from .= "\n\t\tLIMIT $core_limit\n\t) AS $talias ON (m.source = $talias.source)";
+ ###$from .= "\n\t\tLIMIT $core_limit";
+ $from .= "\n\t) AS $talias ON (m.source = ${talias}.source)";
my %used_bumps;
}
$where .= '(' . $talias . ".id IS NOT NULL";
- $where .= ' AND ' . join(' AND ', map {"$talias.value ~* ".$self->QueryParser->quote_value($_)} @{$node->phrases}) if (@{$node->phrases});
+ $where .= ' AND ' . join(' AND ', map {"${talias}.value ~* ".$self->QueryParser->quote_value($_)} @{$node->phrases}) if (@{$node->phrases});
$where .= ')';
push @rank_list, $node_rank;
my $table = $node->table;
my $talias = $node->table_alias;
- $from .= "\n\tJOIN (\n\t\tSELECT * /* facet */\n\t\t FROM metabib.facet_entry\n\t\t WHERE ".
- "SUBSTRING(value,1,1024) IN (" . join(",", map { $self->QueryParser->quote_value($_) } @{$node->values}) . ")".
- "\n\t\t\tAND field IN (SELECT id FROM config.metabib_field WHERE field_class = ". $self->QueryParser->quote_value($node->classname) ." AND facet_field";
-
+ my @field_ids;
if (@{$node->fields} > 0) {
- $from .= " AND name IN (";
- $from .= join(",", map { $self->QueryParser->quote_value($_) } @{$node->fields}) . ")";
+ push(@field_ids, $self->QueryParser->facet_field_ids_by_class( $node->classname, $_ )->[0]) for (@{$node->fields});
+ } else {
+ @field_ids = @{ $self->QueryParser->facet_field_ids_by_class( $node->classname ) };
}
- $from .= ")";
-
- $from .= "\n\t\t) AS $talias ON (m.source = $talias.source)";
+ $from .= "\n\tJOIN /* facet */ metabib.facet_entry $talias ON (\n\t\tm.source = ${talias}.source\n\t\t".
+ "AND SUBSTRING(${talias}.value,1,1024) IN (" . join(",", map { $self->QueryParser->quote_value($_) } @{$node->values}) . ")\n\t\t".
+ "AND ${talias}.field IN (". join(',', @field_ids) . ")\n\t)";
$where .= 'TRUE';
sub rank {
my $self = shift;
return $self->{rank} if ($self->{rank});
- return $self->{rank} = 'rank(' . $self->table_alias . '.index_vector, ' . $self->tsquery . ')';
+ return $self->{rank} = 'rank(' . $self->table_alias . '.index_vector, ' . $self->table_alias . '.tsq)';
}
my $fifo = shift();
my $holdsort = isTrue($fifo) ?
- "CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.request_time, h.selection_depth DESC, p.prox " :
- "p.prox, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.selection_depth DESC, h.request_time ";
+ "pgt.hold_priority, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.request_time, h.selection_depth DESC, p.prox " :
+ "p.prox, pgt.hold_priority, CASE WHEN h.cut_in_line IS TRUE THEN 0 ELSE 1 END, h.selection_depth DESC, h.request_time ";
my $ids = action::hold_request->db_Main->selectcol_arrayref(<<" SQL", {}, $here, $cp, $age);
SELECT h.id
FROM action.hold_request h
JOIN actor.org_unit_proximity p ON (p.from_org = ? AND p.to_org = h.pickup_lib)
JOIN action.hold_copy_map hm ON (hm.hold = h.id)
+ JOIN actor.usr au ON (au.id = h.usr)
+ JOIN permission.grp_tree pgt ON (au.profile = pgt.id)
WHERE hm.target_copy = ?
AND (AGE(NOW(),h.request_time) >= CAST(? AS INTERVAL) OR p.prox = 0)
AND h.capture_time IS NULL
$$prox_list[0] =
[
grep {
- ''.$_->circ_lib eq $pu_lib
+ ''.$_->circ_lib eq $pu_lib &&
+ ( $_->status == 0 || $_->status == 7 )
} @good_copies
];
- $all_copies = [grep {''.$_->circ_lib ne $pu_lib } @good_copies];
+ $all_copies = [grep { $_->status == 0 || $_->status == 7 } grep {''.$_->circ_lib ne $pu_lib } @good_copies];
# $all_copies is now a list of copies not at the pickup library
my $best = choose_nearest_copy($hold, $prox_list);
my %circ_lib_map = map { (''.$_->circ_lib => 1) } @$all_copies;
my $circ_lib_list = [keys %circ_lib_map];
- my $cstore = OpenSRF::AppSession->connect('open-ils.cstore');
+ my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
# Grab the "biggest" loop for this hold so far
my $current_loop = $cstore->request(
my $locations;
my $statuses;
my %cache = (titles => {}, cns => {});
-sub hold_copy_targeter {
- my $self = shift;
- my $client = shift;
- my $check_expire = shift;
- my $one_hold = shift;
-
- $self->{user_filter} = OpenSRF::AppSession->create('open-ils.circ');
- $self->{user_filter}->connect;
- $self->{client} = $client;
-
- my $time = time;
- $check_expire ||= '12h';
- $check_expire = interval_to_seconds( $check_expire );
-
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time() - $check_expire);
- $year += 1900;
- $mon += 1;
- my $expire_threshold = sprintf(
- '%s-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d-00',
- $year, $mon, $mday, $hour, $min, $sec
- );
-
-
- $statuses ||= [ config::copy_status->search(holdable => 't') ];
-
- $locations ||= [ asset::copy_location->search(holdable => 't') ];
-
- my $holds;
-
- %cache = (titles => {}, cns => {});
-
- try {
- if ($one_hold) {
- $holds = [ action::hold_request->search(id => $one_hold) ];
- } else {
- $holds = [ action::hold_request->search_where(
- { capture_time => undef,
- prev_check_time => { '<=' => $expire_threshold },
- },
- { order_by => 'request_time,prev_check_time' } ) ];
- push @$holds, action::hold_request->search_where(
- { capture_time => undef,
- prev_check_time => undef,
- },
- { order_by => 'request_time' } );
- }
- } catch Error with {
- my $e = shift;
- die "Could not retrieve uncaptured hold requests:\n\n$e\n";
- };
-
- for my $hold (@$holds) {
- try {
- #action::hold_request->db_Main->begin_work;
- if ($self->method_lookup('open-ils.storage.transaction.current')->run) {
- $client->respond("Cleaning up after previous transaction\n");
- $self->method_lookup('open-ils.storage.transaction.rollback')->run;
- }
- $self->method_lookup('open-ils.storage.transaction.begin')->run( $client );
- $client->respond("Processing hold ".$hold->id."...\n");
-
- my $copies;
-
- $copies = $self->metarecord_hold_capture($hold) if ($hold->hold_type eq 'M');
- $self->{client}->status( new OpenSRF::DomainObject::oilsContinueStatus );
-
- $copies = $self->title_hold_capture($hold) if ($hold->hold_type eq 'T');
- $self->{client}->status( new OpenSRF::DomainObject::oilsContinueStatus );
-
- $copies = $self->volume_hold_capture($hold) if ($hold->hold_type eq 'V');
- $self->{client}->status( new OpenSRF::DomainObject::oilsContinueStatus );
-
- $copies = $self->copy_hold_capture($hold) if ($hold->hold_type eq 'C');
-
- unless (ref $copies || !@$copies) {
- $client->respond("\tNo copies available for targeting at all!\n");
- }
-
- my @good_copies;
- for my $c (@$copies) {
- next if ( grep {$c->id == $hold->current_copy} @good_copies);
- push @good_copies, $c if ($c);
- }
-
- $client->respond("\t".scalar(@good_copies)." (non-current) copies available for targeting...\n");
-
- my $old_best = $hold->current_copy;
- $hold->update({ current_copy => undef });
-
- if (!scalar(@good_copies)) {
- $client->respond("\tNo (non-current) copies available to fill the hold.\n");
- if ( $old_best && grep {$_->id == $hold->current_copy} @$copies ) {
- $client->respond("\tPushing current_copy back onto the targeting list\n");
- push @good_copies, asset::copy->retrieve( $old_best );
- } else {
- $client->respond("\tcurrent_copy is no longer available for targeting... NEXT HOLD, PLEASE!\n");
- next;
- }
- }
-
- my $prox_list;
- $$prox_list[0] = [grep {$_->circ_lib == $hold->pickup_lib } @good_copies];
- $copies = [grep {$_->circ_lib != $hold->pickup_lib } @good_copies];
-
- my $best = choose_nearest_copy($hold, $prox_list);
-
- if (!$best) {
- $prox_list = create_prox_list( $self, $hold->pickup_lib, $copies );
- $best = choose_nearest_copy($hold, $prox_list);
- }
-
- if ($old_best) {
- # hold wasn't fulfilled, record the fact
-
- $client->respond("\tHold was not (but should have been) fulfilled by ".$old_best->id.".\n");
- action::unfulfilled_hold_list->create(
- { hold => ''.$hold->id,
- current_copy => ''.$old_best->id,
- circ_lib => ''.$old_best->circ_lib,
- });
- }
-
- if ($best) {
- $hold->update( { current_copy => ''.$best->id } );
- $client->respond("\tTargeting copy ".$best->id." for hold fulfillment.\n");
- }
-
- $hold->update( { prev_check_time => 'now' } );
- $client->respond("\tUpdating hold ".$hold->id." with new 'current_copy' for hold fulfillment.\n");
-
- $client->respond("\tProcessing of hold ".$hold->id." complete.\n");
- $self->method_lookup('open-ils.storage.transaction.commit')->run;
-
- #action::hold_request->dbi_commit;
-
- } otherwise {
- my $e = shift;
- $log->error("Processing of hold failed: $e");
- $client->respond("\tProcessing of hold failed!.\n\t\t$e\n");
- $self->method_lookup('open-ils.storage.transaction.rollback')->run;
- #action::hold_request->dbi_rollback;
- };
- }
-
- $self->{user_filter}->disconnect;
- $self->{user_filter}->finish;
- delete $$self{user_filter};
- return undef;
-}
-#__PACKAGE__->register_method(
-# api_name => 'open-ils.storage.action.hold_request.copy_targeter',
-# api_level => 0,
-# stream => 1,
-# method => 'hold_copy_targeter',
-#);
-
sub copy_hold_capture {
my $self = shift;
for my $p ( 0 .. int( scalar(@$prox_list) - 1) ) {
next unless (ref $$prox_list[$p]);
- my @capturable = grep { $_->status == 0 || $_->status == 7 } @{ $$prox_list[$p] };
+ my @capturable = @{ $$prox_list[$p] };
next unless (@capturable);
my $rand = int(rand(scalar(@capturable)));
- while (my ($c) = splice(@capturable,$rand)) {
- return $c if ( OpenILS::Utils::PermitHold::permit_copy_hold(
+ my %seen = ();
+ while (my ($c) = splice(@capturable, $rand, 1)) {
+ return $c if !exists($seen{$c->id}) && ( OpenILS::Utils::PermitHold::permit_copy_hold(
{ title => $c->call_number->record->to_fieldmapper,
title_descriptor => $c->call_number->record->record_descriptor->next->to_fieldmapper,
patron => $hold->usr->to_fieldmapper,
copy => $c->to_fieldmapper,
requestor => $hold->requestor->to_fieldmapper,
request_lib => $hold->request_lib->to_fieldmapper,
- pickup_lib => $hold->pickup_lib->id,
+ pickup_lib => $hold->pickup_lib->id,
+ retarget => 1
}
));
+ $seen{$c->id}++;
last unless(@capturable);
$rand = int(rand(scalar(@capturable)));
my ($prox) = $self->method_lookup('open-ils.storage.asset.copy.proximity')->run( $cp, $lib );
next unless (defined($prox));
+ my $copy_circ_lib = ''.$cp->circ_lib;
# Fetch the weighting value for hold targeting, defaulting to 1
- $self->{target_weight}{$lib} ||= $actor->request(
- 'open-ils.actor.ou_setting.ancestor_default' => $lib.'' => 'circ.holds.org_unit_target_weight'
+ $self->{target_weight}{$copy_circ_lib} ||= $actor->request(
+ 'open-ils.actor.ou_setting.ancestor_default' => $copy_circ_lib.'' => 'circ.holds.org_unit_target_weight'
)->gather(1);
- $self->{target_weight}{$lib} = $self->{target_weight}{$lib}{value} if (ref $self->{target_weight}{$lib});
- $self->{target_weight}{$lib} ||= 1;
+ $self->{target_weight}{$copy_circ_lib} = $self->{target_weight}{$copy_circ_lib}{value} if (ref $self->{target_weight}{$copy_circ_lib});
+ $self->{target_weight}{$copy_circ_lib} ||= 1;
$prox_list[$prox] = [] unless defined($prox_list[$prox]);
- for my $w ( 1 .. $self->{target_weight}{$lib} ) {
+ for my $w ( 1 .. $self->{target_weight}{$copy_circ_lib} ) {
push @{$prox_list[$prox]}, $cp;
}
}
$table cn
where
not deleted
- and (upper(label) > ? or ( cn.id > ? and upper(label) = ? ))
+ and (oils_text_as_bytea(upper(label)) > ? or ( cn.id > ? and oils_text_as_bytea(upper(label)) = ? ))
and owning_lib in ($orgs)
- order by upper(label), 4, 2
+ order by oils_text_as_bytea(upper(label)), 4, 2
limit $size;
SQL
$table cn
where
not deleted
- and (upper(label) < ? or ( cn.id < ? and upper(label) = ? ))
+ and (oils_text_as_bytea(upper(label)) < ? or ( cn.id < ? and oils_text_as_bytea(upper(label)) = ? ))
and owning_lib in ($orgs)
- order by upper(label) desc, 4 desc, 2 desc
+ order by oils_text_as_bytea(upper(label)) desc, 4 desc, 2 desc
limit $size
) as bar
order by 1,4,2;
$table cn
where
not deleted
- and upper(label) < ?
+ and oils_text_as_bytea(upper(label)) < ?
and owning_lib in ($orgs)
- order by upper(label) desc, 4 desc, 2 desc
+ order by oils_text_as_bytea(upper(label)) desc, 4 desc, 2 desc
limit $topsize
) as bar
order by 1,4,2;
$table cn
where
not deleted
- and upper(label) >= ?
+ and oils_text_as_bytea(upper(label)) >= ?
and owning_lib in ($orgs)
- order by upper(label),4,2
+ order by oils_text_as_bytea(upper(label)),4,2
limit $bottomsize;
SQL
for my $t ( @tags ) {
for my $search ( @searches ) {
my $sf = $$search{subfield};
- my $term = NFD(lc($$search{term}));
-
- $term =~ s/\pM+//sgo;
- $term =~ s/\pC+//sgo;
- $term =~ s/\W+$//o;
+ my $term = OpenILS::Application::Storage::FTS::naco_normalize($$search{term}, $sf);
$tag = [$tag] if (!ref($tag));
item_type,
item_form,
quality,
- FIRST(COALESCE(LTRIM(SUBSTR( value, COALESCE(SUBSTRING(ind2 FROM '\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')) AS title
+ FIRST(COALESCE(LTRIM(SUBSTR( value, COALESCE(SUBSTRING(ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')) AS title
FROM (
SELECT rd.record,
rd.item_type,
if (lc($sort) eq 'pubdate') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(SUBSTRING(frp.value FROM '\\\\d+'),'9999')::INT
+ SELECT COALESCE(SUBSTRING(frp.value FROM E'\\\\d+'),'9999')::INT
FROM $metabib_full_rec frp
WHERE frp.record = f.record
AND frp.tag = '260'
} elsif (lc($sort) eq 'title') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')
+ SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'zzzzzzzz')
FROM $metabib_full_rec frt
WHERE frt.record = f.record
AND frt.tag = '245'
if (lc($sort) eq 'pubdate') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(SUBSTRING(frp.value FROM '\\\\d+'),'$number_default_sort')::INT
+ SELECT COALESCE(SUBSTRING(frp.value FROM E'\\\\d+'),'$number_default_sort')::INT
FROM $metabib_full_rec frp
WHERE frp.record = mr.master_record
AND frp.tag = '260'
} elsif (lc($sort) eq 'title') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+ SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
FROM $metabib_full_rec frt
WHERE frt.record = mr.master_record
AND frt.tag = '245'
my $secondary_sort = <<" SORT";
( FIRST ((
- SELECT COALESCE(LTRIM(SUBSTR( sfrt.value, COALESCE(SUBSTRING(sfrt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+ SELECT COALESCE(LTRIM(SUBSTR( sfrt.value, COALESCE(SUBSTRING(sfrt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
FROM $metabib_full_rec sfrt,
$metabib_metarecord mr
WHERE sfrt.record = mr.master_record
if (lc($sort) eq 'pubdate') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(SUBSTRING(frp.value FROM '\\\\d+'),'$number_default_sort')::INT
+ SELECT COALESCE(SUBSTRING(frp.value FROM E'\\\\d+'),'$number_default_sort')::INT
FROM $metabib_full_rec frp
WHERE frp.record = mr.master_record
AND frp.tag = '260'
} elsif (lc($sort) eq 'title') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+ SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
FROM $metabib_full_rec frt
WHERE frt.record = mr.master_record
AND frt.tag = '245'
RANK
$secondary_sort = <<" SORT";
( FIRST ((
- SELECT COALESCE(SUBSTRING(sfrp.value FROM '\\\\d+'),'$number_default_sort')::INT
+ SELECT COALESCE(SUBSTRING(sfrp.value FROM E'\\\\d+'),'$number_default_sort')::INT
FROM $metabib_full_rec sfrp
WHERE sfrp.record = mr.master_record
AND sfrp.tag = '260'
if (lc($sort) eq 'pubdate') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(SUBSTRING(frp.value FROM '\\\\d{4}'),'$number_default_sort')::INT
+ SELECT COALESCE(SUBSTRING(frp.value FROM E'\\\\d{4}'),'$number_default_sort')::INT
FROM $metabib_full_rec frp
WHERE frp.record = b.id
AND frp.tag = '260'
} elsif (lc($sort) eq 'title') {
$rank = <<" RANK";
( FIRST ((
- SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM '\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
+ SELECT COALESCE(LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\\\d+'),'0')::INT + 1 )),'$string_default_sort')
FROM $metabib_full_rec frt
WHERE frt.record = b.id
AND frt.tag = '245'
my $metarecord = ($self->api_name =~ /metabib/ or $query->parse_tree->find_modifier('metabib') or $query->parse_tree->find_modifier('metarecord')) ? "'t'" : "'f'";
my $sth = metabib::metarecord_source_map->db_Main->prepare(<<" SQL");
- SELECT *
+ SELECT * /* bib search */
FROM search.query_parser_fts(
$param_search_ou\:\:INT,
$param_depth\:\:INT,
for my $alias ( @{$pkg->search_field_aliases->{$class}{$field}} ) {
$alias = qr/$alias/;
- s/\b$alias[:=]/$class\|$field:/g;
+ s/(^|\s+)$alias[:=]/$1$class\|$field:/g;
}
}
},
{ flesh => 1,
flesh_fields => { acn => [qw/record owning_lib/] },
- order_by => { acn => "label_sortkey, upper(label) desc, id desc, owning_lib desc" },
+ order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(upper(label)) desc, id desc, owning_lib desc" },
limit => $before_limit,
offset => abs($page) * $page_size - $before_offset,
}
},
{ flesh => 1,
flesh_fields => { acn => [qw/record owning_lib/] },
- order_by => { acn => "label_sortkey, upper(label), id, owning_lib" },
+ order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(upper(label)), id, owning_lib" },
limit => $after_limit,
offset => abs($page) * $page_size - $after_offset,
}
},
{ flesh => 1,
flesh_fields => { acn => [qw/record owning_lib/] },
- order_by => { acn => "label_sortkey, upper(label) desc, id desc, owning_lib desc" },
+ order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(upper(label)) desc, id desc, owning_lib desc" },
limit => $limit,
offset => $offset,
}
},
{ flesh => 1,
flesh_fields => { acn => [qw/record owning_lib/] },
- order_by => { acn => "label_sortkey, upper(label), id, owning_lib" },
+ order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(upper(label)), id, owning_lib" },
limit => $limit,
offset => $offset,
}
use OpenSRF::Utils::JSON;
use OpenSRF::AppSession;
+use OpenSRF::MultiSession;
use OpenSRF::Utils::SettingsClient;
use OpenSRF::Utils::Logger qw/$logger/;
use OpenSRF::Utils qw/:datetime/;
my $log = 'OpenSRF::Utils::Logger';
+my $parallel_collect;
+my $parallel_react;
-sub initialize {}
+sub initialize {
+
+ my $conf = OpenSRF::Utils::SettingsClient->new;
+ $parallel_collect = $conf->config_value( apps => 'open-ils.trigger' => app_settings => parallel => 'collect') || 1;
+ $parallel_react = $conf->config_value( apps => 'open-ils.trigger' => app_settings => parallel => 'react') || 1;
+
+}
sub child_init {}
sub create_active_events_for_object {
my $uid = $target->$ufield;
$uid = $uid->id if (ref $uid); # fleshed user object, unflesh it
- my $opt_in_setting = $editor->search_actor_usr_setting(
+ my $opt_in_setting = $editor->search_actor_user_setting(
{ usr => $uid,
name => $def->opt_in_setting,
value => 'true'
my $uid = $target->$ufield;
$uid = $uid->id if (ref $uid); # fleshed user object, unflesh it
- my $opt_in_setting = $editor->search_actor_usr_setting(
+ my $opt_in_setting = $editor->search_actor_user_setting(
{ usr => $uid,
name => $def->opt_in_setting,
value => 'true'
for (grep { $_ ne '-and' } keys %{$$filter{event}});
}
- my $e = new_editor();
+ my $e = new_editor(xact=>1);
my $events = $e->json_query($query);
'-exists' => {
from => 'aus',
where => {
- name => $def->id,
+ name => $def->opt_in_setting,
usr => { '=' => { '+' . $hook_hash{$def->hook}->core_type => $def->usr_field } },
value=> 'true'
}
$event->event_def( $def->id );
$event->run_time( $run_time );
$event->user_data( OpenSRF::Utils::JSON->perl2JSON($user_data) ) if (defined($user_data));
- $event->granularity($granularity) if (defined $granularity);
$editor->create_action_trigger_event( $event );
}
$e->editor->disconnect;
+ OpenILS::Application::Trigger::Event->ClearObjectCache();
return {
valid => $e->valid,
}
$e->editor->disconnect;
+ OpenILS::Application::Trigger::Event->ClearObjectCache();
return {
valid => $e->valid,
my $self = shift;
my $client = shift;
my $granularity = shift;
-
- my $editor = new_editor();
+ my $granflag = shift;
my $query = [{ state => 'pending', run_time => {'<' => 'now'} }, { order_by => { atev => [ qw/run_time add_time/] }, 'join' => 'atevdef' }];
if (defined $granularity) {
- $query->[0]->{'+atevdef'} = {'-or' => [ {granularity => $granularity}, {granularity => undef} ] };
+ if ($granflag) {
+ $query->[0]->{'+atevdef'} = {granularity => $granularity};
+ } else {
+ $query->[0]->{'+atevdef'} = {'-or' => [ {granularity => $granularity}, {granularity => undef} ] };
+ }
} else {
$query->[0]->{'+atevdef'} = {granularity => undef};
}
- return $editor->search_action_trigger_event(
+ return new_editor(xact=>1)->search_action_trigger_event(
$query, { idlist=> 1, timeout => 7200, substream => 1 }
);
}
api_level=> 1
);
+sub gather_events {
+ my $self = shift;
+ my $client = shift;
+ my $e_ids = shift;
+
+ $e_ids = [$e_ids] if (!ref($e_ids));
+
+ my @events;
+ for my $e_id (@$e_ids) {
+ my $e;
+ try {
+ $e = OpenILS::Application::Trigger::Event->new($e_id);
+ } catch Error with {
+ $logger->error("trigger: Event creation failed with ".shift());
+ };
+
+ next if !$e or $e->event->state eq 'invalid';
+
+ try {
+ $e->build_environment;
+ } catch Error with {
+ $logger->error("trigger: Event environment building failed with ".shift());
+ };
+
+ $e->editor->disconnect;
+ $e->environment->{EventProcessor} = undef; # remove circular ref for json encoding
+ $client->respond($e);
+ }
+
+ OpenILS::Application::Trigger::Event->ClearObjectCache();
+
+ return undef;
+}
+__PACKAGE__->register_method(
+ api_name => 'open-ils.trigger.event.gather',
+ method => 'gather_events',
+ api_level=> 1
+);
+
sub grouped_events {
my $self = shift;
my $client = shift;
my $granularity = shift;
+ my $granflag = shift;
- my ($events) = $self->method_lookup('open-ils.trigger.event.find_pending')->run($granularity);
+ my ($events) = $self->method_lookup('open-ils.trigger.event.find_pending')->run($granularity, $granflag);
my %groups = ( '*' => [] );
return \%groups;
}
- for my $e_id ( @$events ) {
- $logger->info("trigger: processing event $e_id");
+ my @fleshed_events;
- # let the client know we're still chugging along TODO add osrf support for method_lookup $client's
+ if ($parallel_collect == 1 or @$events == 1) { # use method lookup
+ @fleshed_events = $self->method_lookup('open-ils.trigger.event.gather')->run($events);
+ } else {
+ my $self_multi = OpenSRF::MultiSession->new(
+ app => 'open-ils.trigger',
+ cap => $parallel_collect,
+ success_handler => sub {
+ my $self = shift;
+ my $req = shift;
+
+ push @fleshed_events,
+ map { OpenILS::Application::Trigger::Event->new($_) }
+ map { $_->content }
+ @{ $req->{response} };
+ },
+ );
+
+ $self_multi->request( 'open-ils.trigger.event.gather' => $_ ) for ( @$events );
$client->status( new OpenSRF::DomainObject::oilsContinueStatus );
- my $e;
- try {
- $e = OpenILS::Application::Trigger::Event->new($e_id);
- } catch Error with {
- $logger->error("trigger: Event creation failed with ".shift());
- };
-
- next unless $e;
-
- try {
- $e->build_environment;
- } catch Error with {
- $logger->error("trigger: Event environment building failed with ".shift());
- };
+ $self_multi->session_wait(1);
+ $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+ }
+ for my $e (@fleshed_events) {
if (my $group = $e->event->event_def->group_field) {
# split the grouping link steps
my @steps = split /\./, $group;
+ my $group_field = pop(@steps); # we didn't flesh to this, it's a field not an object
+
+ my $node;
+ eval {
+ $node = $e->target;
+ $node = $node->$_() for ( @steps );
+ };
- # find the grouping object
- my $node = $e->target;
- $node = $node->$_() for ( @steps );
+ unless($node) { # should not get here, but to be safe..
+ $e->update_state('invalid');
+ next;
+ }
- # get the pkey value for the grouping object on this event
- my $node_ident = $node->Identity;
- my $ident_value = $node->$node_ident();
+ # get the grouping value for the grouping object on this event
+ my $ident_value = $node->$group_field();
+ if(ref $ident_value) {
+ my $ident_field = $ident_value->Identity;
+ $ident_value = $ident_value->$ident_field()
+ }
- # push this event onto the event+grouping_pkey_value stack
+ # push this event onto the event+grouping_value stack
$groups{$e->event->event_def->id}{$ident_value} ||= [];
push @{ $groups{$e->event->event_def->id}{$ident_value} }, $e;
} else {
# it's a non-grouped event
push @{ $groups{'*'} }, $e;
}
-
- $e->editor->disconnect;
}
+
return \%groups;
}
__PACKAGE__->register_method(
my $self = shift;
my $client = shift;
my $granularity = shift;
-
- my ($groups) = $self->method_lookup('open-ils.trigger.event.find_pending_by_group')->run($granularity);
+ my $granflag = shift;
+
+ my ($groups) = $self->method_lookup('open-ils.trigger.event.find_pending_by_group')->run($granularity, $granflag);
+ $client->respond({"status" => "found"}) if (keys(%$groups) > 1 || @{$$groups{'*'}});
+
+ my $self_multi;
+ if ($parallel_react > 1 and (keys(%$groups) > 1 || @{$$groups{'*'}} > 1)) {
+ $self_multi = OpenSRF::MultiSession->new(
+ app => 'open-ils.trigger',
+ cap => $parallel_react,
+ session_hash_function => sub {
+ my $args = shift;
+ return $args->{target_id};
+ },
+ success_handler => sub {
+ my $me = shift;
+ my $req = shift;
+ $client->respond( $req->{response}->[0]->content );
+ }
+ );
+ }
for my $def ( keys %$groups ) {
if ($def eq '*') {
$logger->info("trigger: run_all_events firing un-grouped events");
for my $event ( @{ $$groups{'*'} } ) {
try {
- $client->respond(
- $self
- ->method_lookup('open-ils.trigger.event.fire')
- ->run($event)
- );
+ if ($self_multi) {
+ $event->environment->{EventProcessor} = undef; # remove circular ref for json encoding
+ $self_multi->request({target_id => $event->id}, 'open-ils.trigger.event.fire', $event);
+ } else {
+ $client->respond(
+ $self
+ ->method_lookup('open-ils.trigger.event.fire')
+ ->run($event)
+ );
+ }
} catch Error with {
$logger->error("trigger: event firing failed with ".shift());
};
}
- $logger->info("trigger: run_all_events completed firing un-grouped events");
+ $logger->info("trigger: run_all_events completed queuing un-grouped events");
+ $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
} else {
my $defgroup = $$groups{$def};
$logger->info("trigger: run_all_events firing events for grouped event def=$def");
for my $ident ( keys %$defgroup ) {
+ $logger->info("trigger: run_all_events firing group for grouped event def=$def and grp ident $ident");
try {
- $client->respond(
- $self
- ->method_lookup('open-ils.trigger.event_group.fire')
- ->run($$defgroup{$ident})
- );
+ if ($self_multi) {
+ $_->environment->{EventProcessor} = undef for @{$$defgroup{$ident}}; # remove circular ref for json encoding
+ $self_multi->request({target_id => $ident}, 'open-ils.trigger.event_group.fire', $$defgroup{$ident});
+ } else {
+ $client->respond(
+ $self
+ ->method_lookup('open-ils.trigger.event_group.fire')
+ ->run($$defgroup{$ident})
+ );
+ }
+ $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
} catch Error with {
$logger->error("trigger: event firing failed with ".shift());
};
}
- $logger->info("trigger: run_all_events completed firing events for grouped event def=$def");
+ $logger->info("trigger: run_all_events completed queuing events for grouped event def=$def");
}
}
-
-
+
+ $self_multi->session_wait(1) if ($self_multi);
+ $logger->info("trigger: run_all_events completed firing events");
+
+ $client->respond_complete();
+ return undef;
}
__PACKAGE__->register_method(
api_name => 'open-ils.trigger.event.run_all_pending',
use strict; use warnings;
use OpenSRF::EX qw/:try/;
use OpenSRF::Utils::JSON;
-
use OpenSRF::Utils::Logger qw/$logger/;
-
use OpenILS::Utils::Fieldmapper;
use OpenILS::Utils::CStoreEditor q/:funcs/;
use OpenILS::Application::Trigger::ModRunner;
-
use Safe;
my $log = 'OpenSRF::Utils::Logger';
my $editor = shift;
$class = ref($class) || $class;
- return $id if (ref($id) && ref($id) eq $class);
-
my $standalone = $editor ? 0 : 1;
$editor ||= new_editor();
+ if (ref($id) && ref($id) eq $class) {
+ $id->environment->{EventProcessor} = $id
+ if ($id->environment->{complete}); # in case it came over an opensrf tube
+ $id->editor( $editor );
+ $id->standalone( $standalone );
+ return $id;
+ }
+
my $self = bless { id => $id, editor => $editor, standalone => $standalone } => $class;
return $self->init()
return $self if (!$self->id);
+ if ($self->standalone) {
+ $self->editor->xact_begin || return undef;
+ }
+
$self->event(
$self->editor->retrieve_action_trigger_event([
$self->id, {
])
);
+ if ($self->standalone) {
+ $self->editor->xact_rollback || return undef;
+ }
+
$self->user_data(OpenSRF::Utils::JSON->JSON2perl( $self->event->user_data ))
if (defined( $self->event->user_data ));
$meth =~ s/Fieldmapper:://;
$meth =~ s/::/_/;
+ if ($self->standalone) {
+ $self->editor->xact_begin || return undef;
+ }
+
$self->target( $self->editor->$meth( $self->event->target ) );
+ if ($self->standalone) {
+ $self->editor->xact_rollback || return undef;
+ }
+
+ unless($self->target) {
+ $self->update_state('invalid');
+ $self->valid(0);
+ }
+
return $self;
}
$e->state( $state );
$e->clear_start_time() if ($e->state eq 'pending');
+ $e->complete_time( 'now' ) if ($e->state eq 'complete');
my $ok = $self->editor->update_action_trigger_event( $e );
if (!$ok) {
if ($self->event->event_def->group_field) {
my @group_path = split(/\./, $self->event->event_def->group_field);
- my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path );
+ pop(@group_path); # the last part is a field, should not get fleshed
+ my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path ) if (@group_path);
}
$self->environment->{complete} = 1;
return $class;
}
+my %_object_by_path_cache = ();
+sub ClearObjectCache {
+ for my $did ( keys %_object_by_path_cache ) {
+ my $phash = $_object_by_path_cache{$did};
+ for my $path ( keys %$phash ) {
+ my $shash = $$phash{$path};
+ for my $step ( keys %$shash ) {
+ my $fhash = $$shash{$step};
+ for my $ffield ( keys %$fhash ) {
+ my $lhash = $$fhash{$ffield};
+ for my $lfield ( keys %$lhash ) {
+ delete $$lhash{$lfield};
+ }
+ delete $$fhash{$ffield};
+ }
+ delete $$shash{$step};
+ }
+ delete $$phash{$path};
+ }
+ delete $_object_by_path_cache{$did};
+ }
+}
+
sub _object_by_path {
my $self = shift;
my $context = shift;
my $collector = shift;
my $label = shift;
my $path = shift;
+ my $ed = shift;
+ my $outer = 0;
+ if (!$ed) {
+ $ed = new_editor(xact=>1);
+ $outer = 1;
+ }
my $step = shift(@$path);
-
my $fhint = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
my $fclass = $self->_fm_class_by_hint( $fhint );
$meth =~ s/Fieldmapper:://;
$meth =~ s/::/_/g;
- my $ed = grep( /open-ils.cstore/, @{$fclass->Controller} ) ?
- $self->editor :
- new_rstore_editor();
-
my $obj = $context->$step();
$logger->debug(
);
if (!ref $obj) {
- $obj = $ed->$meth(
- ($multi) ?
- { $ffield => $context->$lfield() } :
- $context->$lfield()
- );
+
+ my $lval = $context->$lfield();
+
+ if(defined $lval) {
+
+ my $def_id = $self->event->event_def->id;
+ my $str_path = join('.', @$path);
+
+ $obj = $_object_by_path_cache{$def_id}{$str_path}{$step}{$ffield}{$lval} ||
+ $ed->$meth( ($multi) ? { $ffield => $lval } : $lval);
+
+ $_object_by_path_cache{$def_id}{$str_path}{$step}{$ffield}{$lval} ||= $obj;
+ }
}
if (@$path) {
for (@$obj_list) {
my @path_clone = @$path;
- $self->_object_by_path( $_, $collector, $label, \@path_clone );
+ $self->_object_by_path( $_, $collector, $label, \@path_clone, $ed );
}
$obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
}
}
+ $ed->rollback if ($outer);
return $obj;
}
OpenILS::Application::Trigger::Event->new($_, $editor)
} @ids
],
- ids => \@ids,
+ ids => [ map { ref($_) ? $_->id : $_ } @ids ],
editor => $editor
} => $class;
if (scalar(@oks) < scalar(@{ $self->ids })) {
$self->editor->xact_rollback;
return undef;
- } else {
- $ok = $self->editor->xact_commit;
- }
+ }
my $updated = $self->editor->retrieve_action_trigger_event($last_updated);
+ $ok = $self->editor->xact_commit;
+
if ($ok) {
for my $event ( @{ $self->events } ) {
my $e = $event->event;
# returns the calculated copy price
get_copy_price => sub {
my $copy_id = shift;
- return $U->get_copy_price(new_editor(), $copy_id);
+ return $U->get_copy_price(new_editor(xact=>1), $copy_id);
},
# given a copy, returns the title and author in a hash
get_copy_bib_basics => sub {
my $copy_id = shift;
- my $copy = new_editor()->retrieve_asset_copy([
+ my $copy = new_editor(xact=>1)->retrieve_asset_copy([
$copy_id,
{
flesh => 2,
my $t_o = Fieldmapper::action_trigger::event_output->new;
$t_o->data( ($error) ? $error : $output );
$t_o->is_error( ($error) ? 't' : 'f' );
+ $logger->info("trigger: writing " . length($t_o->data) . " bytes to template output");
$env->{EventProcessor}->editor->xact_begin;
$t_o = $env->{EventProcessor}->editor->create_action_trigger_event_output( $t_o );
return $e->die_event unless $e->allowed('CREATE_BIB_IMPORT_QUEUE');
$owner ||= $e->requestor->id;
- return OpenILS::Event->new('BIB_QUEUE_EXISTS')
- if $e->search_vandelay_bib_queue(
- {name => $name, owner => $owner, queue_type => $type})->[0];
+ if ($e->search_vandelay_bib_queue( {name => $name, owner => $owner, queue_type => $type})->[0]) {
+ $e->rollback;
+ return OpenILS::Event->new('BIB_QUEUE_EXISTS')
+ }
my $queue = new Fieldmapper::vandelay::bib_queue();
$queue->name( $name );
return $e->die_event unless $e->allowed('CREATE_AUTHORITY_IMPORT_QUEUE');
$owner ||= $e->requestor->id;
- return OpenILS::Event->new('AUTH_QUEUE_EXISTS')
- if $e->search_vandelay_bib_queue(
- {name => $name, owner => $owner, queue_type => $type})->[0];
+ if ($e->search_vandelay_bib_queue({name => $name, owner => $owner, queue_type => $type})->[0]) {
+ $e->rollback;
+ return OpenILS::Event->new('AUTH_QUEUE_EXISTS')
+ }
my $queue = new Fieldmapper::vandelay::authority_queue();
$queue->name( $name );
}
my $evt = check_queue_perms($e, $type, $queue);
- return $evt if $evt;
+ return $evt if ($evt);
my $cache = new OpenSRF::Utils::Cache();
sub retrieve_queued_records {
my($self, $conn, $auth, $queue_id, $options) = @_;
- my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
+ my $e = new_editor(authtoken => $auth, xact => 1);
+ return $e->die_event unless $e->checkauth;
$options ||= {};
my $limit = $$options{limit} || 20;
my $offset = $$options{offset} || 0;
$queue = $e->retrieve_vandelay_authority_queue($queue_id) or return $e->die_event;
}
my $evt = check_queue_perms($e, $type, $queue);
- return $evt if $evt;
+ return $evt if ($evt);
my $class = ($type eq 'bib') ? 'vqbr' : 'vqar';
my $search = ($type eq 'bib') ?
$rec->clear_marc if $$options{clear_marc};
$conn->respond($rec);
}
+ $e->rollback;
return undef;
}
sub import_record_list {
my($self, $conn, $auth, $rec_ids, $args) = @_;
- my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
+ my $e = new_editor(authtoken => $auth, xact => 1);
+ return $e->die_event unless $e->checkauth;
$args ||= {};
my $err = import_record_list_impl($self, $conn, $rec_ids, $e->requestor, $args);
+ $e->rollback;
return $err if $err;
return {complete => 1};
}
);
sub import_queue {
my($self, $conn, $auth, $q_id, $options) = @_;
- my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
+ my $e = new_editor(authtoken => $auth, xact => 1);
+ return $e->die_event unless $e->checkauth;
$options ||= {};
my $type = $self->{record_type};
my $class = ($type eq 'bib') ? 'vqbr' : 'vqar';
'search_vandelay_queued_bib_record' : 'search_vandelay_queued_authority_record';
my $rec_ids = $e->$search($query, {idlist => 1});
my $err = import_record_list_impl($self, $conn, $rec_ids, $e->requestor, $options);
+ $e->rollback;
return $err if $err;
return {complete => 1};
}
my %bib_sources;
my $editor = new_editor();
my $sources = $editor->search_config_bib_source({id => {'!=' => undef}});
+
foreach my $src (@$sources) {
$bib_sources{$src->id} = $src->source;
}
$rec_class = 'vqar';
}
+ my @success_rec_ids;
for my $rec_id (@$rec_ids) {
my $overlay_target = $overlay_map->{$rec_id};
]);
unless($rec) {
- $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->die_event});
+ $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->event});
$e->rollback;
next;
}
if($U->event_code($record)) {
$e->event($record);
+ $e->rollback;
} else {
}
if($imported) {
+ push @success_rec_ids, $rec_id;
$e->commit;
} else {
- $e->rollback;
# Send an update whenever there's an error
- $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->die_event});
+ $conn->respond({total => $total, progress => ++$count, imported => $rec_id, err_event => $e->event});
}
$conn->respond({total => $total, progress => $count, imported => $rec_id}) if (!$report_all and ++$count % $step) == 0;
$e->rollback;
}
+ import_record_asset_list_impl($conn, \@success_rec_ids, $requestor);
+
$conn->respond({total => $total, progress => $count});
return undef;
}
sub owner_queue_retrieve {
my($self, $conn, $auth, $owner_id, $filters) = @_;
- my $e = new_editor(authtoken => $auth);
+ my $e = new_editor(authtoken => $auth, xact => 1);
return $e->die_event unless $e->checkauth;
$owner_id = $e->requestor->id; # XXX add support for viewing other's queues?
my $queues;
[$search, {order_by => {vaq => 'lower(name)'}}]);
}
$conn->respond($_) for @$queues;
+ $e->rollback;
return undef;
}
sub queued_record_html {
my($self, $conn, $auth, $rec_id) = @_;
- my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
+ my $e = new_editor(xact=>1,authtoken => $auth);
+ return $e->die_event unless $e->checkauth;
my $rec;
if($self->{record_type} eq 'bib') {
$rec = $e->retrieve_vandelay_queued_bib_record($rec_id)
- or return $e->event;
+ or return $e->die_event;
} else {
$rec = $e->retrieve_vandelay_queued_authority_record($rec_id)
- or return $e->event;
+ or return $e->die_event;
}
+ $e->rollback;
return $U->simplereq(
'open-ils.search',
'open-ils.search.biblio.record.html', undef, 1, $rec->marc);
sub retrieve_queue_summary {
my($self, $conn, $auth, $queue_id) = @_;
- my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
+ my $e = new_editor(xact=>1, authtoken => $auth);
+ return $e->die_event unless $e->checkauth;
my $queue;
my $type = $self->{record_type};
if($type eq 'bib') {
$queue = $e->retrieve_vandelay_bib_queue($queue_id)
- or return $e->event;
+ or return $e->die_event;
} else {
$queue = $e->retrieve_vandelay_authority_queue($queue_id)
- or return $e->event;
+ or return $e->die_event;
}
my $evt = check_queue_perms($e, $type, $queue);
__PACKAGE__->register_method(
api_name => "open-ils.vandelay.bib_record.list.asset.import",
- method => 'import_record_list_assets',
+ method => 'noop_import_items',
api_level => 1,
argc => 2,
stream => 1,
);
__PACKAGE__->register_method(
api_name => "open-ils.vandelay.bib_record.queue.asset.import",
- method => 'import_record_queue_assets',
+ method => 'noop_import_items',
api_level => 1,
argc => 2,
stream => 1,
record_type => 'bib'
);
-sub import_record_list_assets {
- my($self, $conn, $auth, $import_def, $rec_ids) = @_;
- my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
- my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
- return $err if $err;
- return {complete => 1};
-}
-
-sub import_record_queue_assets {
- my($self, $conn, $auth, $import_def, $q_id) = @_;
- my $e = new_editor(authtoken => $auth);
- return $e->event unless $e->checkauth;
- my $rec_ids = $e->search_vandelay_queued_bib_record(
- {queue => $q_id, import_time => {'!=' => undef}}, {idlist => 1});
- my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
- return $err if $err;
- return {complete => 1};
-}
+sub noop_import_items { return {complete => 1} }
+
+#sub import_record_list_assets {
+# my($self, $conn, $auth, $import_def, $rec_ids) = @_;
+# my $e = new_editor(xact=>1, authtoken => $auth);
+# return $e->die_event unless $e->checkauth;
+# my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
+# $e->rollback;
+# return $err if $err;
+# return {complete => 1};
+#}
+#
+#sub import_record_queue_assets {
+# my($self, $conn, $auth, $import_def, $q_id) = @_;
+# my $e = new_editor(xact=>1, authtoken => $auth);
+# return $e->die_event unless $e->checkauth;
+# my $rec_ids = $e->search_vandelay_queued_bib_record(
+# {queue => $q_id, import_time => {'!=' => undef}}, {idlist => 1});
+# my $err = import_record_asset_list_impl($conn, $import_def, $rec_ids, $e->requestor);
+# $e->rollback;
+# return $err if $err;
+# return {complete => 1};
+#}
# --------------------------------------------------------------------------------
# Given a list of queued record IDs, imports all items attached to those records
# --------------------------------------------------------------------------------
sub import_record_asset_list_impl {
- my($conn, $import_def, $rec_ids, $requestor) = @_;
+ my($conn, $rec_ids, $requestor) = @_;
my $total = @$rec_ids;
my $try_count = 0;
my $in_count = 0;
- my $roe = new_editor(requestor => $requestor);
+ my $roe = new_editor(xact=> 1, requestor => $requestor);
for my $rec_id (@$rec_ids) {
my $rec = $roe->retrieve_vandelay_queued_bib_record($rec_id);
next unless $rec and $rec->import_time;
- my $item_ids = $roe->search_vandelay_import_item({definition => $import_def, record => $rec->id}, {idlist=>1});
+ my $item_ids = $roe->search_vandelay_import_item({record => $rec->id}, {idlist=>1});
for my $item_id (@$item_ids) {
my $e = new_editor(requestor => $requestor, xact => 1);
respond_with_status($conn, $total, $try_count, ++$in_count, undef, imported_as => $copy->id);
}
}
+ $roe->rollback;
return undef;
}
$xact->do_checkin( $self, $inst_id, $trans_date, $return_date, $current_loc, $item_props );
if ($xact->ok) {
- $xact->patron($self->find_patron($item->{patron}));
+ $xact->patron($self->find_patron(usr => $xact->{circ_user_id})) if $xact->{circ_user_id};
delete $item->{patron};
delete $item->{due_date};
syslog('LOG_INFO', "OILS: Checkin succeeded");
syslog('LOG_DEBUG', "OILS: Open circulation exists on $item_id : user = $bc");
}
- $self->{id} = $item_id;
- $self->{copy} = $copy;
- $self->{volume} = $copy->call_number;
- $self->{record} = $copy->call_number->record;
+ $self->{id} = $item_id;
+ $self->{copy} = $copy;
+ $self->{volume} = $copy->call_number;
+ $self->{record} = $copy->call_number->record;
$self->{call_number} = $copy->call_number->label;
- $self->{mods} = $U->record_to_mvr($self->{record}) if $self->{record}->marc;
+ $self->{mods} = $U->record_to_mvr($self->{record}) if $self->{record}->marc;
+ $self->{transit} = $self->fetch_transit;
+ $self->{hold} = $self->fetch_hold;
+
# use the non-translated version of the copy location as the
# collection code, since it may be used for additional routing
$e->retrieve_asset_copy_location([
$copy->location, {no_i18n => 1}])->name;
- if ($copy->status->id == OILS_COPY_STATUS_IN_TRANSIT) {
- my $transit = $e->search_action_transit_copy([
- {
- target_copy => $copy->id, # NOT barcode ($self->id)
- dest_recv_time => undef
- },
- {
- flesh => 1,
- flesh_fields => {
- atc => [ 'dest' ]
- }
- }
- ])->[0];
- if ($transit) {
- $self->{transit} = $transit;
- $self->{destination_loc} = $transit->dest->shortname;
- } else {
- syslog('LOG_WARNING', "OILS: Item('$item_id') status is In Transit, but no action.transit_copy found!");
- }
+ if($self->{transit}) {
+ $self->{destination_loc} = $self->{transit}->dest->shortname;
+
+ } elsif($self->{hold}) {
+ $self->{destination_loc} = $self->{hold}->pickup_lib->shortname;
}
syslog("LOG_DEBUG", "OILS: Item('$item_id'): found with title '%s'", $self->title_id);
return $self;
}
+# fetch copy transit
+sub fetch_transit {
+ my $self = shift;
+ my $copy = $self->{copy} or return;
+ my $e = OpenILS::SIP->editor();
+
+ if ($copy->status->id == OILS_COPY_STATUS_IN_TRANSIT) {
+ my $transit = $e->search_action_transit_copy([
+ {
+ target_copy => $copy->id, # NOT barcode ($self->id)
+ dest_recv_time => undef
+ },
+ {
+ flesh => 1,
+ flesh_fields => {
+ atc => ['dest']
+ }
+ }
+ ])->[0];
+
+ syslog('LOG_WARNING', "OILS: Item(".$copy->barcode.
+ ") status is In Transit, but no action.transit_copy found!") unless $transit;
+
+ return $transit;
+ }
+
+ return undef;
+}
+
+# fetch captured hold.
+# Assume transit has already beeen fetched
+sub fetch_hold {
+ my $self = shift;
+ my $copy = $self->{copy} or return;
+ my $e = OpenILS::SIP->editor();
+
+ if( ($copy->status->id == OILS_COPY_STATUS_ON_HOLDS_SHELF) ||
+ ($self->{transit} and $self->{transit}->copy_status == OILS_COPY_STATUS_ON_HOLDS_SHELF) ) {
+ # item has been captured for a hold
+
+ my $hold = $e->search_action_hold_request([
+ {
+ current_copy => $copy->id,
+ capture_time => {'!=' => undef},
+ cancel_time => undef,
+ fulfillment_time => undef
+ },
+ {
+ limit => 1,
+ flesh => 1,
+ flesh_fields => {
+ ahr => ['pickup_lib']
+ }
+ }
+ ])->[0];
+
+ syslog('LOG_WARNING', "OILS: Item(".$copy->barcode.
+ ") is captured for a hold, but there is no matching hold request") unless $hold;
+
+ return $hold;
+ }
+
+ return undef;
+}
+
sub run_attr_script {
my $self = shift;
return 1 if $self->{ran_script};
sub hold_pickup_date {
my $self = shift;
my $copy = $self->{copy};
+ my $hold = $self->{hold} or return 0;
- if( ($copy->status->id == OILS_COPY_STATUS_ON_HOLDS_SHELF) ||
- ($self->{transit} and $self->{transit}->copy_status == OILS_COPY_STATUS_ON_HOLDS_SHELF) ) {
+ my $date = $hold->shelf_expire_time;
- # item has been captured for a hold
+ if(!$date) {
+ # hold has not hit the shelf. create a best guess.
- my $e = OpenILS::SIP->editor();
- my $hold = $e->search_action_hold_request([
- {
- current_copy => $copy->id,
- capture_time => {'!=' => undef},
- cancel_time => undef,
- fulfillment_time => undef
- },
- {limit => 1}
- ])->[0];
-
- if($hold) {
- my $date = $hold->shelf_expire_time;
-
- if(!$date) {
- # hold has not hit the shelf. create a best guess.
-
- my $interval = $shelf_expire_setting_cache{$hold->pickup_lib} ||
- $U->ou_ancestor_setting_value(
- $hold->pickup_lib,
- 'circ.holds.default_shelf_expire_interval');
+ my $interval = $shelf_expire_setting_cache{$hold->pickup_lib->id} ||
+ $U->ou_ancestor_setting_value(
+ $hold->pickup_lib->id,
+ 'circ.holds.default_shelf_expire_interval');
- $shelf_expire_setting_cache{$hold->pickup_lib} = $interval;
+ $shelf_expire_setting_cache{$hold->pickup_lib->id} = $interval;
- if($interval) {
- my $seconds = OpenSRF::Utils->interval_to_seconds($interval);
- $date = DateTime->now->add(seconds => $seconds);
- $date = $date->strftime('%FT%T%z') if $date;
- }
- }
-
- return OpenILS::SIP->format_date($date) if $date;
+ if($interval) {
+ my $seconds = OpenSRF::Utils->interval_to_seconds($interval);
+ $date = DateTime->now->add(seconds => $seconds);
+ $date = $date->strftime('%FT%T%z') if $date;
}
}
+ return OpenILS::SIP->format_date($date) if $date;
+
return 0;
}
if ($key ne 'usr' and $key ne 'barcode') {
syslog("LOG_ERROR", "Patron (card) lookup requested by illegeal key '$key'");
- return;
+ return undef;
+ }
+
+ unless(defined $patron_id) {
+ syslog("LOG_WARNING", "No patron ID provided to ILS::Patron->new");
+ return undef;
}
my $type = ref($class) || $class;
syslog("LOG_DEBUG", "OILS: new OpenILS Patron(%s => %s): searching...", $key, $patron_id);
my $e = OpenILS::SIP->editor();
+
+ my $user_id = $patron_id;
+ if($key eq 'barcode') {
+ my $card = $e->search_actor_card({barcode => $patron_id})->[0];
+ unless($card) {
+ syslog("LOG_WARNING", "No such patron barcode: $patron_id");
+ return undef;
+ }
+ $user_id = $card->usr;
+ }
- my $c = $e->search_actor_card({$key => $patron_id}, {idlist=>1});
- my $user;
-
- if( @$c ) {
-
- $user = $e->search_actor_user(
- [
- { card => $$c[0] },
- {
- flesh => 2,
- flesh_fields => {
- "au" => [
- #"cards",
- "card",
- "standing_penalties",
- "addresses",
- "billing_address",
- "mailing_address",
- #"stat_cat_entries",
- 'profile',
- ],
- ausp => ['standing_penalty']
- }
- }
- ]
- );
-
- $user = (@$user) ? $$user[0] : undef;
- }
+ my $user = $e->retrieve_actor_user([
+ $user_id,
+ {
+ flesh => 2,
+ flesh_fields => {
+ au => [
+ "card",
+ "standing_penalties",
+ "addresses",
+ "billing_address",
+ "mailing_address",
+ 'profile',
+ ],
+ ausp => ['standing_penalty']
+ }
+ }
+ ]);
if(!$user) {
syslog("LOG_WARNING", "OILS: Unable to find patron %s => %s", $key, $patron_id);
return $self->{user}->card->active eq 'f';
}
-sub recall_overdue {
+sub recall_overdue { # not implemented
my $self = shift;
return 0;
}
-
sub check_password {
my ($self, $pwd) = @_;
syslog('LOG_DEBUG', 'OILS: Patron->check_password()');
+ return 0 unless (defined $pwd and $self->{user});
return md5_hex($pwd) eq $self->{user}->passwd;
}
-
-sub currency {
+sub currency { # not really implemented
my $self = shift;
syslog('LOG_DEBUG', 'OILS: Patron->currency()');
return 'USD';
return 'OK';
}
-sub print_line {
+sub print_line { # not implemented
my $self = shift;
return '';
}
-sub too_many_charged {
+sub too_many_charged { # not implemented
my $self = shift;
return 0;
}
@{$self}{keys %fields} = values %fields; # copying defaults into object
+ $self->load_override_events;
+
return bless $self, $class;
}
return !$self->{item}->magnetic;
}
+my %override_events;
+sub load_override_events {
+ return if %override_events;
+ my $override = OpenILS::SIP->config->{implementation_config}->{checkin_override};
+ return unless $override;
+ my $events = $override->{event};
+ $events = [$events] unless ref $events eq 'ARRAY';
+ $override_events{$_} = 1 for @$events;
+}
+
my %org_sn_cache;
sub do_checkin {
my $self = shift;
my $args = {barcode => $self->{item}->id};
+ if($return_date) {
+ # SIP date format is YYYYMMDD. Translate to ISO8601
+ $return_date =~ s/(\d{4})(\d{2})(\d{2}).*/$1-$2-$3/;
+ syslog('LOG_INFO', "Checking in with backdate $return_date");
+ $args->{backdate} = $return_date;
+ }
+
if($current_loc) { # SIP client specified a physical location
my $org_id = (defined $org_sn_cache{$current_loc}) ?
$args->{circ_lib} = $phys_location = $org_id if defined $org_id;
}
- my $resp = $U->simplereq(
- 'open-ils.circ',
- 'open-ils.circ.checkin',
- $self->{authtoken}, $args
- );
+ my $override = 0;
+ my ($resp, $txt, $code);
- if ($debug) {
- my $s = Dumper($resp);
- $s =~ s/\n//mog;
- syslog('LOG_INFO', "OILS: Checkin response: $s");
- }
+ while(1) {
+
+ my $method = 'open-ils.circ.checkin';
+ $method .= '.override' if $override;
+
+ $resp = $U->simplereq('open-ils.circ', $method, $self->{authtoken}, $args);
- # In oddball cases, we can receive an array of events.
- # The first event received should be treated as the main result.
- $resp = $$resp[0] if ref($resp) eq 'ARRAY';
+ if ($debug) {
+ my $s = Dumper($resp);
+ $s =~ s/\n//mog;
+ syslog('LOG_INFO', "OILS: Checkin response: $s");
+ }
- my $code = $U->event_code($resp);
- my $txt = (defined $code) ? $resp->{textcode} : '';
+ # In oddball cases, we can receive an array of events.
+ # The first event received should be treated as the main result.
+ $resp = $$resp[0] if ref($resp) eq 'ARRAY';
+ $code = $U->event_code($resp);
+ $txt = (defined $code) ? $resp->{textcode} : '';
+
+ last if $override;
+
+ if ( $override_events{$txt} ) {
+ $override = 1;
+ } else {
+ last;
+ }
+ }
syslog('LOG_INFO', "OILS: Checkin resulted in event: $txt, phys_location: $phys_location");
my $payload = $resp->{payload} || {};
- # Two places to look for hold data. These are more important and more definitive than above.
- if ($payload->{remote_hold}) {
- # actually only used for checkin at non-owning branch w/ hold at same branch
- $self->item->hold($payload->{remote_hold});
+ my ($circ, $copy);
+
+ if(ref $payload eq 'HASH') {
- } elsif ($payload->{hold}) {
- $self->item->hold($payload->{hold});
+ # Two places to look for hold data. These are more important and more definitive than above.
+ if ($payload->{remote_hold}) {
+ # actually only used for checkin at non-owning branch w/ hold at same branch
+ $self->item->hold($payload->{remote_hold});
+
+ } elsif ($payload->{hold}) {
+ $self->item->hold($payload->{hold});
+ }
+
+ $circ = $resp->{payload}->{circ} || '';
+ $copy = $resp->{payload}->{copy} || '';
}
if ($self->item->hold) {
$self->alert(1) if defined $self->alert_type; # alert_type could be "00", hypothetically
- my $circ = $resp->{payload}->{circ} || '';
- my $copy = $resp->{payload}->{copy} || '';
-
if ( $circ ) {
+ $self->{circ_user_id} = $circ->usr;
$self->ok(1);
} elsif ($txt eq 'NO_CHANGE' or $txt eq 'SUCCESS' or $txt eq 'ROUTE_ITEM') {
$self->ok(1); # NO_CHANGE means it wasn't checked out anyway, no problem
return $self;
}
+sub DESTROY {
+ my $self = shift;
+ $self->reset;
+ return undef;
+}
sub app {
my( $self, $app ) = @_;
# -----------------------------------------------------------------------------
sub rollback {
my $self = shift;
- $self->xact_rollback;
- $self->disconnect;
+ my $err;
+ my $ret;
+ try {
+ $self->xact_rollback;
+ } catch Error with {
+ $err = shift
+ } finally {
+ $ret = $self->disconnect
+ };
+ throw $err if ($err);
+ return $ret;
}
sub disconnect {
# -----------------------------------------------------------------------------
sub finish {
my $self = shift;
- $self->commit;
- $self->reset;
+ my $err;
+ my $ret;
+ try {
+ $self->commit;
+ } catch Error with {
+ $err = shift
+ } finally {
+ $ret = $self->reset
+ };
+ throw $err if ($err);
+ return $ret;
}
my $penalties = $e->json_query({from => ['actor.calculate_system_penalties',$user_id, $context_org]});
my $user = $e->retrieve_actor_user( $user_id );
- my $ses = OpenSRF::AppSession->create('open-ils.trigger') if (@$penalties);
+ my @existing_penalties = grep { defined $_->{id} } @$penalties;
+ my @wanted_penalties = grep { !defined $_->{id} } @$penalties;
+ my @trigger_events;
my %csp;
- for my $pen_obj (@$penalties) {
-
- next if grep { # leave duplicate penalties in place
- $_->{org_unit} == $pen_obj->{org_unit} and
- $_->{standing_penalty} == $pen_obj->{standing_penalty} and
- ($_->{id} || '') ne ($pen_obj->{id} || '') } @$penalties;
+ for my $pen_obj (@wanted_penalties) {
my $pen = Fieldmapper::actor::user_standing_penalty->new;
$pen->$_($pen_obj->{$_}) for keys %$pen_obj;
- if(defined $pen_obj->{id}) {
- $e->delete_actor_user_standing_penalty($pen) or return $e->die_event;
+ # let's see if this penalty is accounted for already
+ my ($existing) = grep {
+ $_->{org_unit} == $pen_obj->{org_unit} and
+ $_->{standing_penalty} == $pen_obj->{standing_penalty}
+ } @existing_penalties;
+
+ if($existing) {
+ # we have one of these already. Leave it be, but remove it from the
+ # existing set so it's not deleted in the subsequent loop
+ @existing_penalties = grep { $_->{id} ne $existing->{id} } @existing_penalties;
} else {
+
+ # this is a new penalty
$e->create_actor_user_standing_penalty($pen) or return $e->die_event;
- my $csp_obj = $csp{$pen->standing_penalty} ||
+ my $csp_obj = $csp{$pen->standing_penalty} ||
$e->retrieve_config_standing_penalty( $pen->standing_penalty );
# cache for later
$csp{$pen->standing_penalty} = $csp_obj;
- $ses->request(
- 'open-ils.trigger.event.autocreate',
- 'penalty.' . $csp_obj->name,
- $pen,
- $pen->org_unit
- );
+ push(@trigger_events, ['penalty.' . $csp_obj->name, $pen, $pen->org_unit]);
}
}
+ # at this point, any penalties remaining in the existing
+ # penalty set are unaccounted for and should be removed
+ for my $pen_obj (@existing_penalties) {
+ my $pen = Fieldmapper::actor::user_standing_penalty->new;
+ $pen->$_($pen_obj->{$_}) for keys %$pen_obj;
+ $e->delete_actor_user_standing_penalty($pen) or return $e->die_event;
+ }
+
$e->commit if $commit;
+
+ $U->create_events_for_hook($$_[0], $$_[1], $$_[2]) for @trigger_events;
return undef;
}
sub indb_hold_permit {
my $params = shift;
+ my $function = $$params{retarget} ? 'action.hold_retarget_permit_test' : 'action.hold_request_permit_test';
my $patron_id =
ref($$params{patron}) ? $$params{patron}->id : $$params{patron_id};
my $request_lib =
my $HOLD_TEST = {
from => [
- 'action.hold_request_permit_test',
+ $function,
$$params{pickup_lib},
$request_lib,
$$params{copy}->id,
-package OpenILS::Utils::RemoteAccount;
+package OpenILS::Utils::RemoteAccount;
# use OpenSRF::Utils::SettingsClient;
use OpenSRF::Utils::Logger qw/:logger/;
);
-=pod
+=head1 NAME
+
+OpenILS::Utils::RemoteAccount - Encapsulate FTP, SFTP and SSH file transactions for Evergreen
+
+=head1 DESCRIPTION
The Remote Account module attempts to transfer a file to/from a remote server.
Net::uFTP is used, encapsulating the available options of SCP, FTP and SFTP.
-All information is expected to be gathered by the Event Definition through event parameters:
+=head1 PARAMETERS
+
+All information is expected to be supplied by the caller via parameters:
~ remote_host (required)
~ remote_user
~ remote_password
Similarly, specifying an account requires both user and password.
That is, there are no assumed defaults when the latter arguments are used.
-SSH KEYS:
+=head2 SSH KEYS:
-The use of ssh keys is preferred.
+The use of ssh keys is preferred. Explicit specification of connection type will prevent
+multiple attempts to the same server. Therefore, using the type parameter is also recommended.
-We attempt to use SSH keys where they are specified or otherwise found
+If the type is not explicit, we attempt to use SSH keys where they are specified or otherwise found
in the runtime environment. If only one key is specified, we attempt to derive
the corresponding filename based on the ssh-keygen defaults. If either key is
specified, but both are not found (and readable) then the result is failure. If
-no key is specified, but keys are found, the key-based connections will be attempted,
+no key or type is specified, but keys are found, the key-based connections will be attempted,
but failure will be non-fatal.
=cut
my $base = $cgi->url(-base=>1);
$base =~ s/^http:/https:/o;
print "Location: $base".$apache->unparsed_uri."\n\n";
- return Apache2::Const::OK;
+ return Apache2::Const::REDIRECT;
}
if (!$auth_ses) {
--- /dev/null
+package OpenILS::WWW::TemplateBatchBibUpdate;
+use strict;
+use warnings;
+use bytes;
+
+use Apache2::Log;
+use Apache2::Const -compile => qw(OK REDIRECT DECLINED NOT_FOUND :log);
+use APR::Const -compile => qw(:error SUCCESS);
+use APR::Table;
+
+use Apache2::RequestRec ();
+use Apache2::RequestIO ();
+use Apache2::RequestUtil;
+use CGI;
+use Data::Dumper;
+use Text::CSV;
+
+use OpenSRF::EX qw(:try);
+use OpenSRF::Utils qw/:datetime/;
+use OpenSRF::Utils::Cache;
+use OpenSRF::System;
+use OpenSRF::AppSession;
+use XML::LibXML;
+use XML::LibXSLT;
+
+use Encode;
+use Unicode::Normalize;
+use OpenILS::Utils::Fieldmapper;
+use OpenSRF::Utils::Logger qw/$logger/;
+
+use MARC::Record;
+use MARC::File::XML;
+
+use UNIVERSAL::require;
+
+our @formats = qw/USMARC UNIMARC XML BRE/;
+
+# set the bootstrap config and template include directory when
+# this module is loaded
+my $bootstrap;
+
+sub import {
+ my $self = shift;
+ $bootstrap = shift;
+}
+
+
+sub child_init {
+ OpenSRF::System->bootstrap_client( config_file => $bootstrap );
+ Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
+}
+
+sub handler {
+ my $r = shift;
+ my $cgi = new CGI;
+
+ my $authid = $cgi->cookie('ses') || $cgi->param('ses');
+ my $usr = verify_login($authid);
+ return show_template($r) unless ($usr);
+
+ my $template = $cgi->param('template');
+ return show_template($r) unless ($template);
+
+
+ my $rsource = $cgi->param('recordSource');
+ # find some IDs ...
+ my @records;
+
+ if ($rsource eq 'r') {
+ @records = map { $_ ? ($_) : () } $cgi->param('recid');
+ }
+
+ if ($rsource eq 'c') { # try for a file
+ my $file = $cgi->param('idfile');
+ if ($file) {
+ my $col = $cgi->param('idcolumn') || 0;
+ my $csv = new Text::CSV;
+
+ while (<$file>) {
+ $csv->parse($_);
+ my @data = $csv->fields;
+ my $id = $data[$col];
+ $id =~ s/\D+//o;
+ next unless ($id);
+ push @records, $id;
+ }
+ }
+ }
+
+ my $e = OpenSRF::AppSession->connect('open-ils.cstore');
+ $e->request('open-ils.cstore.transaction.begin')->gather(1);
+
+ # still no records ...
+ my $container = $cgi->param('containerid');
+ if ($rsource eq 'b') {
+ if ($container) {
+ my $bucket = $e->request(
+ 'open-ils.cstore.direct.container.biblio_record_entry_bucket.retrieve',
+ $container
+ )->gather(1);
+ unless($bucket) {
+ $e->request('open-ils.cstore.transaction.rollback')->gather(1);
+ $e->disconnect;
+ $r->log->error("No such bucket $container");
+ $logger->error("No such bucket $container");
+ return Apache2::Const::NOT_FOUND;
+ }
+ my $recs = $e->request(
+ 'open-ils.cstore.direct.container.biblio_record_entry_bucket_item.search.atomic',
+ { bucket => $container }
+ )->gather(1);
+ @records = map { ($_->target_biblio_record_entry) } @$recs;
+ }
+ }
+
+ unless (@records) {
+ $e->request('open-ils.cstore.transaction.rollback')->gather(1);
+ $e->disconnect;
+ return show_template($r);
+ }
+
+ # we have a template and some record ids, so...
+
+ # insert the template record
+ my $min_id = $e->request(
+ 'open-ils.cstore.json_query',
+ { select => { bre => [{ column => 'id', transform => 'min', aggregate => 1}] }, from => 'bre' }
+ )->gather(1)->{id} - 1;
+
+ warn "new template bib id = $min_id\n";
+
+ my $tmpl_rec = Fieldmapper::biblio::record_entry->new;
+ $tmpl_rec->id($min_id);
+ $tmpl_rec->deleted('t');
+ $tmpl_rec->active('f');
+ $tmpl_rec->marc($template);
+ $tmpl_rec->creator($usr->id);
+ $tmpl_rec->editor($usr->id);
+
+ warn "about to create bib $min_id\n";
+ $e->request('open-ils.cstore.direct.biblio.record_entry.create', $tmpl_rec )->gather(1);
+
+ # create the new container for the records and the template
+ my $bucket = Fieldmapper::container::biblio_record_entry_bucket->new;
+ $bucket->owner($usr->id);
+ $bucket->btype('template_merge');
+
+ my $bname = $cgi->param('bname') || 'Temporary Merge Bucket '. localtime() . ' ' . $usr->id;
+ $bucket->name($bname);
+
+ $bucket = $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket.create', $bucket )->gather(1);
+
+ # create items in the bucket
+ my $item = Fieldmapper::container::biblio_record_entry_bucket_item->new;
+ $item->bucket($bucket->id);
+ $item->target_biblio_record_entry($min_id);
+
+ $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket_item.create', $item )->gather(1);
+
+ my %seen;
+ for my $r (@records) {
+ next if ($seen{$r});
+ $item->target_biblio_record_entry($r);
+ $e->request('open-ils.cstore.direct.container.biblio_record_entry_bucket_item.create', $item )->gather(1);
+ $seen{$r}++;
+ }
+
+ $e->request('open-ils.cstore.transaction.commit')->gather(1);
+ $e->disconnect;
+
+ # fire the background bucket processor
+ my $cache_key = OpenSRF::AppSession
+ ->create('open-ils.cat')
+ ->request('open-ils.cat.container.template_overlay.background', $authid, $bucket->id)
+ ->gather(1);
+
+ return show_processing_template($r, $bucket->id, \@records, $cache_key);
+}
+
+sub verify_login {
+ my $auth_token = shift;
+ return undef unless $auth_token;
+
+ my $user = OpenSRF::AppSession
+ ->create("open-ils.auth")
+ ->request( "open-ils.auth.session.retrieve", $auth_token )
+ ->gather(1);
+
+ if (ref($user) eq 'HASH' && $user->{ilsevent} == 1001) {
+ return undef;
+ }
+
+ return $user if ref($user);
+ return undef;
+}
+
+sub show_processing_template {
+ my $r = shift;
+ my $bid = shift;
+ my $recs = shift;
+ my $cache_key = shift;
+
+ my $rec_string = @$recs;
+
+ $r->content_type('text/html');
+ $r->print(<<HTML);
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+ <head>
+ <title>Merging records...</title>
+ <style type="text/css">
+ \@import '/js/dojo/dojo/resources/dojo.css';
+ \@import '/js/dojo/dijit/themes/tundra/tundra.css';
+ .hide_me { display: none; visibility: hidden; }
+ th { font-weight: bold; }
+ </style>
+
+ <script type="text/javascript">
+ var djConfig= {
+ isDebug: false,
+ parseOnLoad: true,
+ AutoIDL: ['aou','aout','pgt','au','cbreb']
+ }
+ </script>
+
+ <script src='/js/dojo/dojo/dojo.js'></script>
+ <!-- <script src="/js/dojo/dojo/openils_dojo.js"></script> -->
+
+ <script type="text/javascript">
+
+ dojo.require('fieldmapper.AutoIDL');
+ dojo.require('fieldmapper.dojoData');
+ dojo.require('openils.User');
+ dojo.require('openils.CGI');
+ dojo.require('openils.widget.ProgressDialog');
+
+ var cgi = new openils.CGI();
+ var u = new openils.User({ authcookie : 'ses' });
+
+ dojo.addOnLoad(function () {
+ progress_dialog.show(true);
+ progress_dialog.update({maximum:$rec_string});
+
+ var interval;
+ interval = setInterval( function() {
+ fieldmapper.standardRequest(
+ ['open-ils.actor','open-ils.actor.anon_cache.get_value'],
+ { async : false,
+ params: [ u.authtoken, 'res_list' ],
+ onerror : function (r) { progress_dialog.hide(); },
+ onresponse : function (r) {
+ var counter = { success : 0, fail : 0, total : 0 };
+ dojo.forEach( openils.Util.readResponse(r), function(x) {
+ if (x.complete) {
+ clearInterval(interval);
+ progress_dialog.hide();
+ if (x.success == 't') dojo.byId('complete_msg').innerHTML = 'Overlay completed successfully';
+ else dojo.byId('complete_msg').innerHTML = 'Overlay did not complet successfully';
+ } else {
+ counter.total++;
+ switch (x.success) {
+ case 't':
+ counter.success++;
+ break;
+ default:
+ counter.fail++;
+ break;
+ }
+ }
+ });
+
+ // update the progress dialog
+ progress_dialog.update({progress:counter.total});
+ dojo.byId('success_count').innerHTML = counter.success;
+ dojo.byId('fail_count').innerHTML = counter.fail;
+ dojo.byId('total_count').innerHTML = counter.total;
+ }
+ }
+ );
+ }, 1000);
+
+ });
+ </script>
+ </head>
+
+ <body style="margin:10px;" class='tundra'>
+ <div class="hide_me"><div dojoType="openils.widget.ProgressDialog" jsId="progress_dialog"></div></div>
+
+ <table style="width:100%; margin-top:100px;">
+ <th>
+ <td>Status</td>
+ <td>Record Count</td>
+ </th>
+ <tr>
+ <td>Success</td>
+ <td id='success_count'></td>
+ </tr>
+ <tr>
+ <td>Failure</td>
+ <td id='fail_count'></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td id='total_count'></td>
+ </tr>
+ </table>
+
+ <div id='complete_msg'></div>
+
+ </body>
+</html>
+HTML
+
+ return Apache2::Const::OK;
+}
+
+
+sub show_template {
+ my $r = shift;
+
+ $r->content_type('text/html');
+ $r->print(<<'HTML');
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+ <head>
+ <title>Merge Template Builder</title>
+ <style type="text/css">
+ @import '/js/dojo/dojo/resources/dojo.css';
+ @import '/js/dojo/dijit/themes/tundra/tundra.css';
+ .hide_me { display: none; visibility: hidden; }
+ table.ruleTable th { padding: 5px; border-collapse: collapse; border-bottom: solid 1px gray; font-weight: bold; }
+ table.ruleTable td { padding: 5px; border-collapse: collapse; border-bottom: solid 1px gray; }
+ </style>
+
+ <script type="text/javascript">
+ var djConfig= {
+ isDebug: false,
+ parseOnLoad: true,
+ AutoIDL: ['aou','aout','pgt','au','cbreb']
+ }
+ </script>
+
+ <script src='/js/dojo/dojo/dojo.js'></script>
+ <!-- <script src="/js/dojo/dojo/openils_dojo.js"></script> -->
+
+ <script type="text/javascript">
+
+ dojo.require('dojo.data.ItemFileReadStore');
+ dojo.require('dijit.form.Form');
+ dojo.require('dijit.form.NumberSpinner');
+ dojo.require('dijit.form.FilteringSelect');
+ dojo.require('dijit.form.TextBox');
+ dojo.require('dijit.form.Textarea');
+ dojo.require('dijit.form.Button');
+ dojo.require('MARC.Batch');
+ dojo.require('fieldmapper.AutoIDL');
+ dojo.require('fieldmapper.dojoData');
+ dojo.require('openils.User');
+ dojo.require('openils.CGI');
+
+ var cgi = new openils.CGI();
+ var u = new openils.User({ authcookie : 'ses' });
+
+ var bucketStore = new dojo.data.ItemFileReadStore(
+ { data : cbreb.toStoreData(
+ fieldmapper.standardRequest(
+ ['open-ils.actor','open-ils.actor.container.retrieve_by_class'],
+ [u.authtoken, u.user.id(), 'biblio', 'staff_client']
+ )
+ )
+ }
+ );
+
+ function render_preview () {
+ var rec = ruleset_to_record();
+ dojo.byId('marcPreview').innerHTML = rec.toBreaker();
+ }
+
+ function render_from_template () {
+ var kid_number = dojo.byId('ruleList').childNodes.length;
+ var clone = dojo.query('*[name=ruleTable]', dojo.byId('ruleTemplate'))[0].cloneNode(true);
+
+ var typeSelect = dojo.query('*[name=typeSelect]',clone).instantiate(dijit.form.FilteringSelect, {
+ onChange : function (val) {
+ switch (val) {
+ case 'a':
+ case 'r':
+ dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]).attr('disabled',false);
+ break;
+ default :
+ dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]).attr('disabled',true);
+ };
+ render_preview();
+ }
+ })[0];
+
+ var marcData = dojo.query('*[name=marcData]',clone).instantiate(dijit.form.TextBox, {
+ onChange : render_preview
+ })[0];
+
+
+ var tag = dojo.query('*[name=tag]',clone).instantiate(dijit.form.TextBox, {
+ onChange : function (newtag) {
+ var md = dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]);
+ var current_marc = md.attr('value');
+
+ if (newtag.length == 3) {
+ if (current_marc.length == 0) newtag += ' \\\\';
+ if (current_marc.substr(0,3) != newtag) current_marc = newtag + current_marc.substr(3);
+ }
+ md.attr('value', current_marc);
+ render_preview();
+ }
+ })[0];
+
+ var sf = dojo.query('*[name=sf]',clone).instantiate(dijit.form.TextBox, {
+ onChange : function (newsf) {
+ var md = dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',clone)[0]);
+ var current_marc = md.attr('value');
+ var sf_list = newsf.split('');
+
+ for (var i in sf_list) {
+ var re = '\\$' + sf_list[i];
+ if (current_marc.match(re)) continue;
+ current_marc += '$' + sf_list[i];
+ }
+
+ md.attr('value', current_marc);
+ render_preview();
+ }
+ })[0];
+
+ var matchSF = dojo.query('*[name=matchSF]',clone).instantiate(dijit.form.TextBox, {
+ onChange : render_preview
+ })[0];
+
+ var matchRE = dojo.query('*[name=matchRE]',clone).instantiate(dijit.form.TextBox, {
+ onChange : render_preview
+ })[0];
+
+ var removeButton = dojo.query('*[name=removeButton]',clone).instantiate(dijit.form.Button, {
+ onClick : function() {
+ dojo.addClass(
+ dojo.byId('ruleList').childNodes[kid_number],
+ 'hide_me'
+ );
+ render_preview();
+ }
+ })[0];
+
+ dojo.place(clone,'ruleList');
+ }
+
+ function ruleset_to_record () {
+ var rec = new MARC.Record ({ delimiter : '$' });
+
+ dojo.forEach(
+ dojo.query('#ruleList *[name=ruleTable]').filter( function (node) {
+ if (node.className.match(/hide_me/)) return false;
+ return true;
+ }),
+ function (tbl) {
+ var rule_tag = new MARC.Field ({
+ tag : '905',
+ ind1 : ' ',
+ ind2 : ' '
+ });
+ var rule_txt = dijit.byNode(dojo.query('*[name=tagContainer] .dijit',tbl)[0]).attr('value');
+ rule_txt += dijit.byNode(dojo.query('*[name=sfContainer] .dijit',tbl)[0]).attr('value');
+
+ var reSF = dijit.byNode(dojo.query('*[name=matchSFContainer] .dijit',tbl)[0]).attr('value');
+ if (reSF) {
+ var reRE = dijit.byNode(dojo.query('*[name=matchREContainer] .dijit',tbl)[0]).attr('value');
+ rule_txt += '[' + reSF + '~' + reRE + ']';
+ }
+
+ var rtype = dijit.byNode(dojo.query('*[name=typeSelectContainer] .dijit',tbl)[0]).attr('value');
+ rule_tag.addSubfields( rtype, rule_txt )
+ rec.appendFields( rule_tag );
+
+ if (rtype == 'a' || rtype == 'r') {
+ rec.appendFields(
+ new MARC.Record ({
+ delimiter : '$',
+ marcbreaker : dijit.byNode(dojo.query('*[name=marcDataContainer] .dijit',tbl)[0]).attr('value')
+ }).fields[0]
+ );
+ }
+ }
+ );
+
+ return rec;
+ }
+ </script>
+ </head>
+
+ <body style="margin:10px;" class='tundra'>
+
+ <div dojoType="dijit.form.Form" id="myForm" jsId="myForm" encType="multipart/form-data" action="" method="POST">
+ <script type='dojo/method' event='onSubmit'>
+ var rec = ruleset_to_record();
+
+ if (rec.subfield('905','r') == '') { // no-op to force replace mode
+ rec.appendFields(
+ new MARC.Field ({
+ tag : '905',
+ ind1 : ' ',
+ ind2 : ' ',
+ subfields : [['r','901c']]
+ })
+ );
+ }
+
+ dojo.byId('template_value').value = rec.toXmlString();
+ return true;
+ </script>
+
+ <input type='hidden' id='template_value' name='template'/>
+
+ <label for='inputTypeSelect'>Record source:</label>
+ <select name='recordSource' dojoType='dijit.form.FilteringSelect'>
+ <script type='dojo/method' event='onChange' args="val">
+ switch (val) {
+ case 'b':
+ dojo.removeClass('bucketListContainer','hide_me');
+ dojo.addClass('csvContainer','hide_me');
+ dojo.addClass('recordContainer','hide_me');
+ break;
+ case 'c':
+ dojo.addClass('bucketListContainer','hide_me');
+ dojo.removeClass('csvContainer','hide_me');
+ dojo.addClass('recordContainer','hide_me');
+ break;
+ case 'r':
+ dojo.addClass('bucketListContainer','hide_me');
+ dojo.addClass('csvContainer','hide_me');
+ dojo.removeClass('recordContainer','hide_me');
+ break;
+ };
+ </script>
+ <script type='dojo/method' event='postCreate'>
+ if (cgi.param('recordSource')) {
+ this.attr('value',cgi.param('recordSource'));
+ this.onChange(cgi.param('recordSource'));
+ }
+ </script>
+ <option value='b'>a Bucket</option>
+ <option value='c'>a CSV File</option>
+ <option value='r'>a specific record ID</option>
+ </select>
+
+ <table style='margin:10px; margin-bottom:20px;'>
+<!--
+ <tr>
+ <th>Merge template name (optional):</th>
+ <td><input id='bucketName' jsId='bucketName' type='text' dojoType='dijit.form.TextBox' name='bname' value=''/></td>
+ </tr>
+-->
+ <tr class='' id='bucketListContainer'>
+ <td>Bucket named:
+ <div name='containerid' jsId='bucketList' dojoType='dijit.form.FilteringSelect' store='bucketStore' searchAttr='name' id='bucketList'>
+ <script type='dojo/method' event='postCreate'>
+ if (cgi.param('containerid')) this.attr('value',cgi.param('containerid'));
+ </script>
+ </div>
+ </td>
+ </tr>
+ <tr class='hide_me' id='csvContainer'>
+ <td>
+ Column <input style='width:75px;' type='text' dojoType='dijit.form.NumberSpinner' name='idcolumn' value='0' constraints='{min:0,max:100,places:0}' /> of:
+ <input id='idfile' type="file" name="idfile"/>
+ <br/>
+ <br/>
+ Columns are numbered starting at 0. For instance, when looking at a CSV file in Excel, the column labeled A is the same as column 0, and the column labeled B is the same as column 1.
+ </td>
+ </tr>
+ <tr class='hide_me' id='recordContainer'>
+ <td>Record ID: <input dojoType='dijit.form.TextBox' name='recid' style='width:75px;' type='text' value=''/></td>
+ </tr>
+ </table>
+
+ <button type="submit" dojoType='dijit.form.Button'>GO!</button> (After setting up your template below.)
+
+ <br/>
+ <br/>
+
+ </div> <!-- end of the form -->
+
+ <hr/>
+ <table style='width: 100%'>
+ <tr>
+ <td style='width: 50%'><div id='ruleList'></div></td>
+ <td valign='top'>Update Template Preview:<br/><pre id='marcPreview'></pre></td>
+ </tr>
+ </table>
+
+ <button dojoType='dijit.form.Button'>Add Merge Rule
+ <script type='dojo/connect' event='onClick'>render_from_template()</script>
+ <script type='dojo/method' event='postCreate'>render_from_template()</script>
+ </button>
+
+ <div class='hide_me' id='ruleTemplate'>
+ <div name='ruleTable'>
+ <table class='ruleTable'>
+ <tbody>
+ <tr>
+ <th style="text-align:center;">Rule Setup</th>
+ <th style="text-align:center;">Data</th>
+ <th style="text-align:center;">Help</th>
+ </tr>
+ <tr>
+ <th>Action (Rule Type)</th>
+ <td name='typeSelectContainer'>
+ <select name='typeSelect'>
+ <option value='r'>Replace</option>
+ <option value='a'>Add</option>
+ <option value='d'>Delete</option>
+ </select>
+ </td>
+ <td>How to change the existing records</td>
+ </tr>
+ <tr>
+ <th>MARC Tag</th>
+ <td name='tagContainer'><input style='with: 2em;' name='tag' type='text'></input</td>
+ <td>Three characters, no spaces, no indicators, etc. eg: 245</td>
+ </td>
+ <tr>
+ <th>Subfields (optional)</th>
+ <td name='sfContainer'><input name='sf' type='text'/></td>
+ <td>No spaces, no delimiters, eg: abcnp</td>
+ </tr>
+ <tr>
+ <th>MARC Data</th>
+ <td name='marcDataContainer'><input name='marcData' type='text'/></td>
+ <td>MARC-Breaker formatted data with indicators and subfield delimiters, eg:<br/>245 04$aThe End</td>
+ </tr>
+ <tr>
+ <th colspan='3' style='padding-top: 20px; text-align: center;'>Advanced Matching Restriction (Optional)</th>
+ </tr>
+ <tr>
+ <th>Subfield</th>
+ <td name='matchSFContainer'><input style='with: 2em;' name='matchSF' type='text'></input</td>
+ <td>A single subfield code, no delimiters, eg: a</td>
+ <tr>
+ <th>Regular Expression</th>
+ <td name='matchREContainer'><input name='matchRE' type='text'/></td>
+ <td>See <a href="http://perldoc.perl.org/perlre.html#Regular-Expressions" target="_blank">the Perl documentation</a>
+ for an explanation of Regular Expressions.
+ </td>
+ </tr>
+ <tr>
+ <td colspan='3' style='padding-top: 20px; text-align: center;'>
+ <button name='removeButton'>Remove this Template Rule</button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <hr/>
+ </div>
+ </div>
+
+ </body>
+</html>
+HTML
+
+ return Apache2::Const::OK;
+}
+
+1;
+
+
$sheetname =~ s/\W/_/gos;
my $sheet = $xls->add_worksheet($sheetname);
+ # don't try to write formulas, just write anything that starts with = as a text cell
+ $sheet->add_write_handler(qr/^=/, sub { return shift->write_string(@_); } );
$sheet->write_row('A1', $r->{column_labels});
select_list := ARRAY_APPEND( select_list, key || '::INT AS key' );
FOR i IN 1 .. ARRAY_UPPER(xpath_list,1) LOOP
- select_list := ARRAY_APPEND(
- select_list,
- $sel$
- EXPLODE_ARRAY(
- COALESCE(
- NULLIF(
- oils_xpath(
- $sel$ ||
- quote_literal(
- CASE
- WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
- ELSE xpath_list[i] || '//text()'
- END
- ) ||
- $sel$,
- $sel$ || document_field || $sel$
+ IF xpath_list[i] = 'null()' THEN
+ select_list := ARRAY_APPEND( select_list, 'NULL::TEXT AS c_' || i );
+ ELSE
+ select_list := ARRAY_APPEND(
+ select_list,
+ $sel$
+ EXPLODE_ARRAY(
+ COALESCE(
+ NULLIF(
+ oils_xpath(
+ $sel$ ||
+ quote_literal(
+ CASE
+ WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
+ ELSE xpath_list[i] || '//text()'
+ END
+ ) ||
+ $sel$,
+ $sel$ || document_field || $sel$
+ ),
+ '{}'::TEXT[]
),
- '{}'::TEXT[]
- ),
- '{NULL}'::TEXT[]
- )
- ) AS c_$sel$ || i
- );
- where_list := ARRAY_APPEND(
- where_list,
- 'c_' || i || ' IS NOT NULL'
- );
+ '{NULL}'::TEXT[]
+ )
+ ) AS c_$sel$ || i
+ );
+ where_list := ARRAY_APPEND(
+ where_list,
+ 'c_' || i || ' IS NOT NULL'
+ );
+ END IF;
END LOOP;
q := $q$
return;
$func$ LANGUAGE PLPERLU;
+CREATE OR REPLACE FUNCTION oils_text_as_bytea (TEXT) RETURNS BYTEA AS $_$
+ SELECT CAST(REGEXP_REPLACE($1, $$\\$$, $$\\\\$$, 'g') AS BYTEA);
+$_$ LANGUAGE SQL IMMUTABLE;
+
COMMIT;
install_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-INSERT INTO config.upgrade_log (version) VALUES ('0412'); -- phasefx
+INSERT INTO config.upgrade_log (version) VALUES ('0439'); -- miker
CREATE TABLE config.bib_source (
id SERIAL PRIMARY KEY,
extended INTERVAL NOT NULL,
normal INTERVAL NOT NULL,
shrt INTERVAL NOT NULL,
- max_renewals INT NOT NULL
+ max_renewals INT NOT NULL,
+ date_ceiling TIMESTAMPTZ
);
COMMENT ON TABLE config.rule_circ_duration IS $$
/*
*/
$$;
+CREATE TABLE config.hard_due_date (
+ id SERIAL PRIMARY KEY,
+ duration_rule INT NOT NULL REFERENCES config.rule_circ_duration (id)
+ DEFERRABLE INITIALLY DEFERRED,
+ ceiling_date TIMESTAMPTZ NOT NULL,
+ active_date TIMESTAMPTZ NOT NULL
+);
+
CREATE TABLE config.rule_max_fine (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE CHECK ( name ~ E'^\\w+$' ),
usergroup BOOL NOT NULL DEFAULT TRUE,
perm_interval INTERVAL DEFAULT '3 years'::interval NOT NULL,
description TEXT,
- application_perm TEXT
+ application_perm TEXT,
+ hold_priority INT NOT NULL DEFAULT 0
);
CREATE INDEX grp_tree_parent_idx ON permission.grp_tree (parent);
CREATE TABLE vandelay.queued_bib_record (
queue INT NOT NULL REFERENCES vandelay.bib_queue (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
bib_source INT REFERENCES config.bib_source (id) DEFERRABLE INITIALLY DEFERRED,
- imported_as INT REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
+ imported_as BIGINT REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED
) INHERITS (vandelay.queued_record);
ALTER TABLE vandelay.queued_bib_record ADD PRIMARY KEY (id);
CREATE INDEX queued_bib_record_queue_idx ON vandelay.queued_bib_record (queue);
for my $f ( keys %fields) {
if ( @{$fields{$f}{sf}} ) {
for my $from_field ($source_r->field( $f )) {
- for my $to_field ($target_r->field( $f )) {
- if (exists($fields{$f}{match})) {
- next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
+ my @tos = $target_r->field( $f );
+ if (!@tos) {
+ my @new_fields = map { $_->clone } $source_r->field( $f );
+ $target_r->insert_fields_ordered( @new_fields );
+ } else {
+ for my $to_field (@tos) {
+ if (exists($fields{$f}{match})) {
+ next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
+ }
+ my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}{sf}};
+ $to_field->add_subfields( @new_sf );
}
- my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}{sf}};
- $to_field->add_subfields( @new_sf );
}
}
} else {
END IF;
END IF;
- add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),''),'');
- strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),''),'');
- replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),''),'');
- preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),''),'');
+ add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),','),'');
+ strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),','),'');
+ replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),','),'');
+ preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),','),'');
output.add_rule := BTRIM(add_rule,',');
output.replace_rule := BTRIM(replace_rule,',');
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' ),
- biblio.marc21_extract_fixed_field( bib_id, 'Date1'),
- biblio.marc21_extract_fixed_field( bib_id, 'Date2');
+ 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;
CREATE INDEX asset_call_number_creator_idx ON asset.call_number (creator);
CREATE INDEX asset_call_number_editor_idx ON asset.call_number (editor);
CREATE INDEX asset_call_number_dewey_idx ON asset.call_number (public.call_number_dewey(label));
-CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (upper(label),id,owning_lib);
-CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(label_sortkey);
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(upper(label)),id,owning_lib);
+CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(oils_text_as_bytea(label_sortkey));
CREATE UNIQUE INDEX asset_call_number_label_once_per_lib ON asset.call_number (record, owning_lib, label) WHERE deleted = FALSE OR deleted IS FALSE;
CREATE RULE protect_cn_delete AS ON DELETE TO asset.call_number DO INSTEAD UPDATE asset.call_number SET deleted = TRUE WHERE OLD.id = asset.call_number.id;
CREATE TRIGGER asset_label_sortkey_trigger
ON UPDATE CASCADE
DEFERRABLE
INITIALLY DEFERRED,
- target_biblio_record_entry INT NOT NULL
+ target_biblio_record_entry BIGINT NOT NULL
REFERENCES biblio.record_entry (id)
ON DELETE CASCADE
ON UPDATE CASCADE
CREATE INDEX circ_all_usr_idx ON action.circulation ( usr );
CREATE INDEX circ_circ_staff_idx ON action.circulation ( circ_staff );
CREATE INDEX circ_checkin_staff_idx ON action.circulation ( checkin_staff );
+CREATE INDEX action_circulation_target_copy_idx ON action.circulation (target_copy);
CREATE UNIQUE INDEX circ_parent_idx ON action.circulation ( parent_circ ) WHERE parent_circ IS NOT NULL;
CREATE UNIQUE INDEX only_one_concurrent_checkout_per_copy ON action.circulation(target_copy) WHERE checkin_time IS NULL;
END;
$$ LANGUAGE PLPGSQL;
-CREATE TRIGGER push_due_date_tgr BEFORE INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
+CREATE TRIGGER push_due_date_tgr BEFORE INSERT OR UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
CREATE TABLE action.aged_circulation (
usr_post_code TEXT,
CREATE INDEX aged_circ_copy_circ_lib_idx ON "action".aged_circulation (copy_circ_lib);
CREATE INDEX aged_circ_copy_owning_lib_idx ON "action".aged_circulation (copy_owning_lib);
CREATE INDEX aged_circ_copy_location_idx ON "action".aged_circulation (copy_location);
+CREATE INDEX action_aged_circulation_target_copy_idx ON action.aged_circulation (target_copy);
CREATE OR REPLACE VIEW action.all_circulation AS
SELECT id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
CREATE INDEX ahn_notify_staff_idx ON action.hold_notification ( notify_staff );
CREATE TABLE action.hold_copy_map (
- id SERIAL PRIMARY KEY,
+ id BIGSERIAL PRIMARY KEY,
hold INT NOT NULL REFERENCES action.hold_request (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
target_copy BIGINT NOT NULL, -- REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, -- XXX could be an serial.issuance
CONSTRAINT copy_once_per_hold UNIQUE (hold,target_copy)
SELECT * INTO circ_chain_tail FROM action.circ_chain(circ_chain_head.id) ORDER BY xact_start DESC LIMIT 1;
EXIT WHEN circ_chain_tail.xact_finish IS NULL;
- -- Now get the user setings, if any, to block purging if the user wants to keep more circs
+ -- Now get the user settings, if any, to block purging if the user wants to keep more circs
usr_keep_age.value := NULL;
SELECT * INTO usr_keep_age FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_age';
ELSIF usr_keep_start.value IS NOT NULL THEN
keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
ELSE
- keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTEVAL );
+ keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTERVAL );
END IF;
EXIT WHEN AGE(NOW(), circ_chain_tail.xact_finish) < keep_age;
DEFERRABLE INITIALLY DEFERRED,
catalog_item BOOLEAN NOT NULL DEFAULT FALSE,
transferable BOOLEAN NOT NULL DEFAULT FALSE,
- record INT REFERENCES biblio.record_entry (id)
+ record BIGINT REFERENCES biblio.record_entry (id)
DEFERRABLE INITIALLY DEFERRED,
- CONSTRAINT brt_name_once_per_owner UNIQUE(owner, name, record)
+ CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record)
);
CREATE TABLE booking.resource (
+-- Before starting the transaction: drop some constraints that
+-- may or may not exist.
+
+DROP INDEX asset.asset_call_number_upper_label_id_owning_lib_idx;
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(upper(label)),id,owning_lib);
+
+
+\qecho Before starting the transaction: drop some constraints.
+\qecho If a DROP fails because the constraint doesn't exist, ignore the failure.
+
+ALTER TABLE permission.grp_perm_map DROP CONSTRAINT grp_perm_map_perm_fkey;
+ALTER TABLE permission.usr_perm_map DROP CONSTRAINT usr_perm_map_perm_fkey;
+ALTER TABLE permission.usr_object_perm_map DROP CONSTRAINT usr_object_perm_map_perm_fkey;
+ALTER TABLE booking.resource_type DROP CONSTRAINT brt_name_or_record_once_per_owner;
+ALTER TABLE booking.resource_type DROP CONSTRAINT brt_name_once_per_owner;
+
+\qecho Beginning the transaction now
+
BEGIN;
--- Highest-numbered individual upgrade script
--- incorporated herein:
+-- Highest-numbered individual upgrade script incorporated herein:
-INSERT INTO config.upgrade_log (version) VALUES ('0403');
+INSERT INTO config.upgrade_log (version) VALUES ('0433');
--- Begin by upgrading permission.perm_list. This is fairly complicated.
+-- Recreate one of the constraints that we just dropped,
+-- under a different name:
+
+ALTER TABLE booking.resource_type
+ ADD CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record);
+
+-- Now upgrade permission.perm_list. This is fairly complicated.
-- Add ON UPDATE CASCADE to some foreign keys so that, when we renumber the
-- permissions, the dependents will follow and stay in sync:
-ALTER TABLE permission.grp_perm_map DROP CONSTRAINT grp_perm_map_perm_fkey;
ALTER TABLE permission.grp_perm_map ADD CONSTRAINT grp_perm_map_perm_fkey FOREIGN KEY (perm)
REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE permission.usr_perm_map DROP CONSTRAINT usr_perm_map_perm_fkey;
ALTER TABLE permission.usr_perm_map ADD CONSTRAINT usr_perm_map_perm_fkey FOREIGN KEY (perm)
REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
-ALTER TABLE permission.usr_object_perm_map DROP CONSTRAINT usr_object_perm_map_perm_fkey;
ALTER TABLE permission.usr_object_perm_map ADD CONSTRAINT usr_object_perm_map_perm_fkey FOREIGN KEY (perm)
REFERENCES permission.perm_list (id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
'View org unit settings related to credit card processing' );
INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 390, 'ADMIN_CREDIT_CARD_PROCESSING',
'Update org unit settings related to credit card processing' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 391, 'ADMIN_SERIAL_CAPTION_PATTERN',
+ 'Create/update/delete serial caption and pattern objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 392, 'ADMIN_SERIAL_SUBSCRIPTION',
+ 'Create/update/delete serial subscription objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 393, 'ADMIN_SERIAL_DISTRIBUTION',
+ 'Create/update/delete serial distribution objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 394, 'ADMIN_SERIAL_STREAM',
+ 'Create/update/delete serial stream objects' );
+INSERT INTO permission.temp_perm ( id, code, description ) VALUES ( 395, 'RECEIVE_SERIAL',
+ 'Receive serial items' );
-- Now for the permissions from the IDL. We don't have descriptions for them.
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 391, 'ADMIN_ACQ_CLAIM' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 392, 'ADMIN_ACQ_CLAIM_EVENT_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 393, 'ADMIN_ACQ_CLAIM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 394, 'ADMIN_ACQ_DISTRIB_FORMULA' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 395, 'ADMIN_ACQ_FISCAL_YEAR' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 396, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 397, 'ADMIN_ACQ_FUND_TAG' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 398, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 399, 'ADMIN_AGE_PROTECT_RULE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 400, 'ADMIN_ASSET_COPY_TEMPLATE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 401, 'ADMIN_BOOKING_RESERVATION_ATTR_MAP' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 402, 'ADMIN_CIRC_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 403, 'ADMIN_CIRC_MOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 404, 'ADMIN_CLAIM_POLICY' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 405, 'ADMIN_CONFIG_REMOTE_ACCOUNT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 406, 'ADMIN_FIELD_DOC' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 407, 'ADMIN_GLOBAL_FLAG' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 408, 'ADMIN_GROUP_PENALTY_THRESHOLD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 409, 'ADMIN_HOLD_CANCEL_CAUSE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 410, 'ADMIN_HOLD_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 411, 'ADMIN_IDENT_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 412, 'ADMIN_IMPORT_ITEM_ATTR_DEF' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 413, 'ADMIN_INDEX_NORMALIZER' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 414, 'ADMIN_INVOICE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 415, 'ADMIN_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 416, 'ADMIN_INVOICE_PAYMENT_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 417, 'ADMIN_LINEITEM_MARC_ATTR_DEF' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 418, 'ADMIN_MARC_CODE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 419, 'ADMIN_MAX_FINE_RULE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 420, 'ADMIN_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 421, 'ADMIN_ORG_UNIT_SETTING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 422, 'ADMIN_RECURRING_FINE_RULE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 423, 'ADMIN_SERIAL_SUBSCRIPTION' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 424, 'ADMIN_STANDING_PENALTY' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 425, 'ADMIN_SURVEY' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 426, 'ADMIN_USER_REQUEST_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 427, 'ADMIN_USER_SETTING_GROUP' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 428, 'ADMIN_USER_SETTING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 429, 'ADMIN_Z3950_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 430, 'CREATE_BIB_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 431, 'CREATE_BIBLIO_FINGERPRINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 432, 'CREATE_BIB_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 433, 'CREATE_BILLING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 434, 'CREATE_CN_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 435, 'CREATE_COPY_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 436, 'CREATE_INVOICE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 437, 'CREATE_INVOICE_ITEM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 438, 'CREATE_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 439, 'CREATE_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 440, 'CREATE_METABIB_CLASS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 441, 'CREATE_METABIB_SEARCH_ALIAS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 442, 'CREATE_USER_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 443, 'DELETE_BIB_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 444, 'DELETE_BIBLIO_FINGERPRINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 445, 'DELETE_BIB_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 446, 'DELETE_BILLING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 447, 'DELETE_CN_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 448, 'DELETE_COPY_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 449, 'DELETE_INVOICE_ITEM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 450, 'DELETE_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 451, 'DELETE_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 452, 'DELETE_METABIB_CLASS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 453, 'DELETE_METABIB_SEARCH_ALIAS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 454, 'DELETE_USER_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 455, 'MANAGE_CLAIM' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 456, 'UPDATE_BIB_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 457, 'UPDATE_BIBLIO_FINGERPRINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 458, 'UPDATE_BIB_SOURCE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 459, 'UPDATE_BILLING_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 460, 'UPDATE_CN_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 461, 'UPDATE_COPY_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 462, 'UPDATE_INVOICE_ITEM_TYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 463, 'UPDATE_INVOICE_METHOD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 464, 'UPDATE_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 465, 'UPDATE_METABIB_CLASS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 466, 'UPDATE_METABIB_SEARCH_ALIAS' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 467, 'UPDATE_USER_BTYPE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 468, 'user_request.create' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 469, 'user_request.delete' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 470, 'user_request.update' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 471, 'user_request.view' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 472, 'VIEW_ACQ_FUND_ALLOCATION_PERCENT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 473, 'VIEW_CIRC_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 474, 'VIEW_CLAIM' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 475, 'VIEW_GROUP_PENALTY_THRESHOLD' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 476, 'VIEW_HOLD_MATRIX_MATCHPOINT' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 477, 'VIEW_INVOICE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 478, 'VIEW_MERGE_PROFILE' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 479, 'VIEW_SERIAL_SUBSCRIPTION' );
-INSERT INTO permission.temp_perm ( id, code ) VALUES ( 480, 'VIEW_STANDING_PENALTY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 396, 'ADMIN_ACQ_CLAIM' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 397, 'ADMIN_ACQ_CLAIM_EVENT_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 398, 'ADMIN_ACQ_CLAIM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 399, 'ADMIN_ACQ_DISTRIB_FORMULA' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 400, 'ADMIN_ACQ_FISCAL_YEAR' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 401, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 402, 'ADMIN_ACQ_FUND_TAG' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 403, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 404, 'ADMIN_AGE_PROTECT_RULE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 405, 'ADMIN_ASSET_COPY_TEMPLATE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 406, 'ADMIN_BOOKING_RESERVATION_ATTR_MAP' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 407, 'ADMIN_CIRC_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 408, 'ADMIN_CIRC_MOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 409, 'ADMIN_CLAIM_POLICY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 410, 'ADMIN_CONFIG_REMOTE_ACCOUNT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 411, 'ADMIN_FIELD_DOC' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 412, 'ADMIN_GLOBAL_FLAG' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 413, 'ADMIN_GROUP_PENALTY_THRESHOLD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 414, 'ADMIN_HOLD_CANCEL_CAUSE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 415, 'ADMIN_HOLD_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 416, 'ADMIN_IDENT_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 417, 'ADMIN_IMPORT_ITEM_ATTR_DEF' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 418, 'ADMIN_INDEX_NORMALIZER' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 419, 'ADMIN_INVOICE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 420, 'ADMIN_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 421, 'ADMIN_INVOICE_PAYMENT_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 422, 'ADMIN_LINEITEM_MARC_ATTR_DEF' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 423, 'ADMIN_MARC_CODE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 424, 'ADMIN_MAX_FINE_RULE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 425, 'ADMIN_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 426, 'ADMIN_ORG_UNIT_SETTING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 427, 'ADMIN_RECURRING_FINE_RULE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 428, 'ADMIN_STANDING_PENALTY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 429, 'ADMIN_SURVEY' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 430, 'ADMIN_USER_REQUEST_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 431, 'ADMIN_USER_SETTING_GROUP' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 432, 'ADMIN_USER_SETTING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 433, 'ADMIN_Z3950_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 434, 'CREATE_BIB_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 435, 'CREATE_BIBLIO_FINGERPRINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 436, 'CREATE_BIB_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 437, 'CREATE_BILLING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 438, 'CREATE_CN_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 439, 'CREATE_COPY_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 440, 'CREATE_INVOICE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 441, 'CREATE_INVOICE_ITEM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 442, 'CREATE_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 443, 'CREATE_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 444, 'CREATE_METABIB_CLASS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 445, 'CREATE_METABIB_SEARCH_ALIAS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 446, 'CREATE_USER_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 447, 'DELETE_BIB_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 448, 'DELETE_BIBLIO_FINGERPRINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 449, 'DELETE_BIB_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 450, 'DELETE_BILLING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 451, 'DELETE_CN_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 452, 'DELETE_COPY_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 453, 'DELETE_INVOICE_ITEM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 454, 'DELETE_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 455, 'DELETE_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 456, 'DELETE_METABIB_CLASS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 457, 'DELETE_METABIB_SEARCH_ALIAS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 458, 'DELETE_USER_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 459, 'MANAGE_CLAIM' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 460, 'UPDATE_BIB_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 461, 'UPDATE_BIBLIO_FINGERPRINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 462, 'UPDATE_BIB_SOURCE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 463, 'UPDATE_BILLING_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 464, 'UPDATE_CN_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 465, 'UPDATE_COPY_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 466, 'UPDATE_INVOICE_ITEM_TYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 467, 'UPDATE_INVOICE_METHOD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 468, 'UPDATE_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 469, 'UPDATE_METABIB_CLASS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 470, 'UPDATE_METABIB_SEARCH_ALIAS' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 471, 'UPDATE_USER_BTYPE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 472, 'user_request.create' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 473, 'user_request.delete' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 474, 'user_request.update' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 475, 'user_request.view' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 476, 'VIEW_ACQ_FUND_ALLOCATION_PERCENT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 477, 'VIEW_CIRC_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 478, 'VIEW_CLAIM' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 479, 'VIEW_GROUP_PENALTY_THRESHOLD' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 480, 'VIEW_HOLD_MATRIX_MATCHPOINT' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 481, 'VIEW_INVOICE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 482, 'VIEW_MERGE_PROFILE' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 483, 'VIEW_SERIAL_SUBSCRIPTION' );
+INSERT INTO permission.temp_perm ( id, code ) VALUES ( 484, 'VIEW_STANDING_PENALTY' );
-- For every permission in the temp_perm table that has a matching
-- permission in the real table: record the original id.
INSERT INTO action_trigger.event_definition
(id, active, owner, name, hook, validator, reactor, cleanup_success, cleanup_failure, delay, delay_field, group_field, template) VALUES
(23, true, 1, 'PO JEDI', 'acqpo.activated', 'Acq::PurchaseOrderEDIRequired', 'GeneratePurchaseOrderJEDI', NULL, NULL, '00:05:00', NULL, NULL,
-$$[%- USE date -%]
-[%# start JEDI document -%]
+$$
+[%- USE date -%]
+[%# start JEDI document
+ # Vendor specific kludges:
+ # BT - vendcode goes to NAD/BY *suffix* w/ 91 qualifier
+ # INGRAM - vendcode goes to NAD/BY *segment* w/ 91 qualifier (separately)
+ # BRODART - vendcode goes to FTX segment (lineitem level)
+-%]
+[%-
+IF target.provider.edi_default.vendcode && target.provider.code == 'BRODART';
+ xtra_ftx = target.provider.edi_default.vendcode;
+END;
+-%]
[%- BLOCK big_block -%]
{
"recipient":"[% target.provider.san %]",
"ORDERS":[ "order", {
"po_number":[% target.id %],
"date":"[% date.format(date.now, '%Y%m%d') %]",
- "buyer":[{
- [%- IF target.provider.edi_default.vendcode -%]
- "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]",
- "id-qualifier": 91
+ "buyer":[
+ [% IF target.provider.edi_default.vendcode && (target.provider.code == 'BT' || target.provider.name.match('(?i)^BAKER & TAYLOR')) -%]
+ {"id-qualifier": 91, "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]"}
+ [%- ELSIF target.provider.edi_default.vendcode && target.provider.code == 'INGRAM' -%]
+ {"id":"[% target.ordering_agency.mailing_address.san %]"},
+ {"id-qualifier": 91, "id":"[% target.provider.edi_default.vendcode %]"}
[%- ELSE -%]
- "id":"[% target.ordering_agency.mailing_address.san %]"
- [%- END -%]
- }],
- "vendor":[
+ {"id":"[% target.ordering_agency.mailing_address.san %]"}
+ [%- END -%]
+ ],
+ "vendor":[
[%- # target.provider.name (target.provider.id) -%]
"[% target.provider.san %]",
{"id-qualifier": 92, "id":"[% target.provider.id %]"}
],
"currency":"[% target.provider.currency_type %]",
+
"items":[
- [% FOR li IN target.lineitems %]
+ [%- FOR li IN target.lineitems %]
{
+ "line_index":"[% li.id %]",
"identifiers":[ [%-# li.isbns = helpers.get_li_isbns(li.attributes) %]
[% FOR isbn IN helpers.get_li_isbns(li.attributes) -%]
[% IF isbn.length == 13 -%]
{"id-qualifier":"IB","id":"[% isbn %]"},
[%- END %]
[% END %]
- {"id-qualifier":"SA","id":"[% li.id %]"}
+ {"id-qualifier":"IN","id":"[% li.id %]"}
],
"price":[% li.estimated_unit_price || '0.00' %],
"desc":[
- {"BTI":"[% helpers.get_li_attr('title', '', li.attributes) %]"},
+ {"BTI":"[% helpers.get_li_attr('title', '', li.attributes) %]"},
{"BPU":"[% helpers.get_li_attr('publisher', '', li.attributes) %]"},
{"BPD":"[% helpers.get_li_attr('pubdate', '', li.attributes) %]"},
{"BPH":"[% helpers.get_li_attr('pagination','', li.attributes) %]"}
],
+ [%- ftx_vals = [];
+ FOR note IN li.lineitem_notes;
+ NEXT UNLESS note.vendor_public == 't';
+ ftx_vals.push(note.value);
+ END;
+ IF xtra_ftx; ftx_vals.unshift(xtra_ftx); END;
+ IF ftx_vals.size == 0; ftx_vals.unshift(''); END; # BT needs FTX+LIN for every LI, even if it is an empty one
+ -%]
+
+ "free-text":[
+ [% FOR note IN ftx_vals -%] "[% note %]"[% UNLESS loop.last %], [% END %][% END %]
+ ],
"quantity":[% li.lineitem_details.size %]
}[% UNLESS loop.last %],[% END %]
[%-# TODO: lineitem details (later) -%]
[% END %]
],
"line_items":[% target.lineitems.size %]
- }] [% # close ORDERS array %]
- }] [% # close body array %]
+ }] [%# close ORDERS array %]
+ }] [%# close body array %]
}
[% END %]
[% tempo = PROCESS big_block; helpers.escape_json(tempo) %]
-$$
-);
+
+$$);
INSERT INTO action_trigger.environment (event_def, path) VALUES
(23, 'lineitems.attributes'),
CREATE OR REPLACE FUNCTION asset.acp_status_changed()
RETURNS TRIGGER AS $$
BEGIN
- IF NEW.status <> OLD.status THEN
- NEW.status_changed_time := now();
- END IF;
- RETURN NEW;
+ IF NEW.status <> OLD.status THEN
+ NEW.status_changed_time := now();
+ END IF;
+ RETURN NEW;
END;
$$ LANGUAGE plpgsql;
ALTER TABLE action.circulation DROP CONSTRAINT action_circulation_target_copy_fkey;
+-- Rebuild dependent views
+
+DROP VIEW IF EXISTS action.billable_circulations;
+
+CREATE OR REPLACE VIEW action.billable_circulations AS
+ SELECT *
+ FROM action.circulation
+ WHERE xact_finish IS NULL;
+
+DROP VIEW IF EXISTS action.open_circulation;
+
+CREATE OR REPLACE VIEW action.open_circulation AS
+ SELECT *
+ FROM action.circulation
+ WHERE checkin_time IS NULL
+ ORDER BY due_date;
+
CREATE OR REPLACE FUNCTION action.age_circ_on_delete () RETURNS TRIGGER AS $$
DECLARE
found char := 'N';
-- Adding circ.holds.target_skip_me OU setting logic to the pre-matchpoint tests
-CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
+DECLARE
+ current_requestor_group permission.grp_tree%ROWTYPE;
+ requestor_object actor.usr%ROWTYPE;
+ user_object actor.usr%ROWTYPE;
+ item_object asset.copy%ROWTYPE;
+ item_cn_object asset.call_number%ROWTYPE;
+ rec_descriptor metabib.rec_descriptor%ROWTYPE;
+ current_mp_weight FLOAT;
+ matchpoint_weight FLOAT;
+ tmp_weight FLOAT;
+ current_mp config.hold_matrix_matchpoint%ROWTYPE;
+ matchpoint config.hold_matrix_matchpoint%ROWTYPE;
+BEGIN
+ SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+ SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
+ SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+ SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+ SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r WHERE r.record = item_cn_object.record;
+
+ PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
+
+ IF NOT FOUND THEN
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = requestor_object.profile;
+ ELSE
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = user_object.profile;
+ END IF;
+
+ LOOP
+ -- for each potential matchpoint for this ou and group ...
+ FOR current_mp IN
+ SELECT m.*
+ FROM config.hold_matrix_matchpoint m
+ WHERE m.requestor_grp = current_requestor_group.id AND m.active
+ ORDER BY CASE WHEN m.circ_modifier IS NOT NULL THEN 16 ELSE 0 END +
+ CASE WHEN m.juvenile_flag IS NOT NULL THEN 16 ELSE 0 END +
+ CASE WHEN m.marc_type IS NOT NULL THEN 8 ELSE 0 END +
+ CASE WHEN m.marc_form IS NOT NULL THEN 4 ELSE 0 END +
+ CASE WHEN m.marc_vr_format IS NOT NULL THEN 2 ELSE 0 END +
+ CASE WHEN m.ref_flag IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
+
+ current_mp_weight := 5.0;
+
+ IF current_mp.circ_modifier IS NOT NULL THEN
+ CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
+ END IF;
+
+ IF current_mp.marc_type IS NOT NULL THEN
+ IF item_object.circ_as_type IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
+ ELSE
+ CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
+ END IF;
+ END IF;
+
+ IF current_mp.marc_form IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
+ END IF;
+
+ IF current_mp.marc_vr_format IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
+ END IF;
+
+ IF current_mp.juvenile_flag IS NOT NULL THEN
+ CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
+ END IF;
+
+ IF current_mp.ref_flag IS NOT NULL THEN
+ CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
+ END IF;
+
+
+ -- caclulate the rule match weight
+ IF current_mp.item_owning_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.item_circ_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.pickup_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.request_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.user_home_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ -- set the matchpoint if we found the best one
+ IF matchpoint_weight IS NULL OR matchpoint_weight > current_mp_weight THEN
+ matchpoint = current_mp;
+ matchpoint_weight = current_mp_weight;
+ END IF;
+
+ END LOOP;
+
+ EXIT WHEN current_requestor_group.parent IS NULL OR matchpoint.id IS NOT NULL;
+
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = current_requestor_group.parent;
+ END LOOP;
+
+ RETURN matchpoint.id;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
DECLARE
matchpoint_id INT;
user_object actor.usr%ROWTYPE;
END IF;
END IF;
- FOR standing_penalty IN
- SELECT DISTINCT csp.*
- FROM actor.usr_standing_penalty usp
- JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
- WHERE usr = match_user
- AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
- AND (usp.stop_date IS NULL or usp.stop_date > NOW())
- AND csp.block_list LIKE '%HOLD%' LOOP
-
- result.fail_part := standing_penalty.name;
- result.success := FALSE;
- done := TRUE;
- RETURN NEXT result;
- END LOOP;
-
- IF hold_test.stop_blocked_user IS TRUE THEN
+ IF NOT retargetting THEN
FOR standing_penalty IN
SELECT DISTINCT csp.*
FROM actor.usr_standing_penalty usp
WHERE usr = match_user
AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
AND (usp.stop_date IS NULL or usp.stop_date > NOW())
- AND csp.block_list LIKE '%CIRC%' LOOP
+ AND csp.block_list LIKE '%HOLD%' LOOP
result.fail_part := standing_penalty.name;
result.success := FALSE;
done := TRUE;
RETURN NEXT result;
END LOOP;
- END IF;
-
- IF hold_test.max_holds IS NOT NULL THEN
- SELECT INTO hold_count COUNT(*)
- FROM action.hold_request
- WHERE usr = match_user
- AND fulfillment_time IS NULL
- AND cancel_time IS NULL
- AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
-
- IF hold_count >= hold_test.max_holds THEN
- result.fail_part := 'config.hold_matrix_test.max_holds';
- result.success := FALSE;
- done := TRUE;
- RETURN NEXT result;
+
+ IF hold_test.stop_blocked_user IS TRUE THEN
+ FOR standing_penalty IN
+ SELECT DISTINCT csp.*
+ FROM actor.usr_standing_penalty usp
+ JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+ WHERE usr = match_user
+ AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+ AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+ AND csp.block_list LIKE '%CIRC%' LOOP
+
+ result.fail_part := standing_penalty.name;
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END LOOP;
+ END IF;
+
+ IF hold_test.max_holds IS NOT NULL THEN
+ SELECT INTO hold_count COUNT(*)
+ FROM action.hold_request
+ WHERE usr = match_user
+ AND fulfillment_time IS NULL
+ AND cancel_time IS NULL
+ AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
+
+ IF hold_count >= hold_test.max_holds THEN
+ result.fail_part := 'config.hold_matrix_test.max_holds';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
END IF;
END IF;
END;
$func$ LANGUAGE plpgsql;
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, FALSE);
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION action.hold_retarget_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, TRUE );
+$func$ LANGUAGE SQL;
+
-- New post-delete trigger to propagate deletions to parent(s)
CREATE OR REPLACE FUNCTION action.age_parent_circ_on_delete () RETURNS TRIGGER AS $$
END;
$func$ LANGUAGE 'plpgsql';
$$;
- RETURN TRUE;
+ RETURN TRUE;
END;
$creator$ LANGUAGE 'plpgsql';
PERFORM auditor.create_auditor_func(sch, tbl);
PERFORM auditor.create_auditor_update_trigger(sch, tbl);
PERFORM auditor.create_auditor_lifecycle(sch, tbl);
- RETURN TRUE;
+ RETURN TRUE;
END;
$creator$ LANGUAGE 'plpgsql';
ALTER TABLE AUDITOR.actor_usr_history ADD COLUMN
claims_never_checked_out_count INT;
-DROP VIEW auditor.actor_usr_lifecycle;
+DROP VIEW IF EXISTS auditor.actor_usr_lifecycle;
SELECT auditor.create_auditor_lifecycle( 'actor', 'usr' );
DEFERRABLE INITIALLY DEFERRED,
max_fine NUMERIC(8,2),
elbow_room INTERVAL,
- CONSTRAINT brt_name_or_record_once_per_owner UNIQUE(owner, name, record)
+ CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record)
);
CREATE TABLE booking.resource (
ALTER TABLE auditor.actor_org_unit_history
ADD COLUMN fiscal_calendar INT;
-DROP VIEW auditor.actor_org_unit_lifecycle;
+DROP VIEW IF EXISTS auditor.actor_org_unit_lifecycle;
SELECT auditor.create_auditor_lifecycle( 'actor', 'org_unit' );
END;
$$ LANGUAGE plpgsql;
+COMMENT ON FUNCTION actor.usr_purge_data(INT, INT) IS $$
+/**
+ * Finds rows dependent on a given row in actor.usr and either deletes them
+ * or reassigns them to a different user.
+ */
+$$;
+
+CREATE OR REPLACE FUNCTION actor.usr_delete(
+ src_usr IN INTEGER,
+ dest_usr IN INTEGER
+) RETURNS VOID AS $$
+DECLARE
+ old_profile actor.usr.profile%type;
+ old_home_ou actor.usr.home_ou%type;
+ new_profile actor.usr.profile%type;
+ new_home_ou actor.usr.home_ou%type;
+ new_name text;
+ new_dob actor.usr.dob%type;
+BEGIN
+ SELECT
+ id || '-PURGED-' || now(),
+ profile,
+ home_ou,
+ dob
+ INTO
+ new_name,
+ old_profile,
+ old_home_ou,
+ new_dob
+ FROM
+ actor.usr
+ WHERE
+ id = src_usr;
+ --
+ -- Quit if no such user
+ --
+ IF old_profile IS NULL THEN
+ RETURN;
+ END IF;
+ --
+ perform actor.usr_purge_data( src_usr, dest_usr );
+ --
+ -- Find the root grp_tree and the root org_unit. This would be simpler if we
+ -- could assume that there is only one root. Theoretically, someday, maybe,
+ -- there could be multiple roots, so we take extra trouble to get the right ones.
+ --
+ SELECT
+ id
+ INTO
+ new_profile
+ FROM
+ permission.grp_ancestors( old_profile )
+ WHERE
+ parent is null;
+ --
+ SELECT
+ id
+ INTO
+ new_home_ou
+ FROM
+ actor.org_unit_ancestors( old_home_ou )
+ WHERE
+ parent_ou is null;
+ --
+ -- Truncate date of birth
+ --
+ IF new_dob IS NOT NULL THEN
+ new_dob := date_trunc( 'year', new_dob );
+ END IF;
+ --
+ UPDATE
+ actor.usr
+ SET
+ card = NULL,
+ profile = new_profile,
+ usrname = new_name,
+ email = NULL,
+ passwd = random()::text,
+ standing = DEFAULT,
+ ident_type =
+ (
+ SELECT MIN( id )
+ FROM config.identification_type
+ ),
+ ident_value = NULL,
+ ident_type2 = NULL,
+ ident_value2 = NULL,
+ net_access_level = DEFAULT,
+ photo_url = NULL,
+ prefix = NULL,
+ first_given_name = new_name,
+ second_given_name = NULL,
+ family_name = new_name,
+ suffix = NULL,
+ alias = NULL,
+ day_phone = NULL,
+ evening_phone = NULL,
+ other_phone = NULL,
+ mailing_address = NULL,
+ billing_address = NULL,
+ home_ou = new_home_ou,
+ dob = new_dob,
+ active = FALSE,
+ master_account = DEFAULT,
+ super_user = DEFAULT,
+ barred = FALSE,
+ deleted = TRUE,
+ juvenile = DEFAULT,
+ usrgroup = 0,
+ claims_returned_count = DEFAULT,
+ credit_forward_balance = DEFAULT,
+ last_xact_id = DEFAULT,
+ alert_message = NULL,
+ create_date = now(),
+ expire_date = now()
+ WHERE
+ id = src_usr;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION actor.usr_delete(INT, INT) IS $$
+/**
+ * Logically deletes a user. Removes personally identifiable information,
+ * and purges associated data in other tables.
+ */
+$$;
+
-- INSERT INTO config.copy_status (id,name) VALUES (15,oils_i18n_gettext(15, 'On reservation shelf', 'ccs', 'name'));
ALTER TABLE acq.fund
$$;
CREATE OR REPLACE VIEW money.billable_xact_summary_location_view AS
- SELECT * FROM money.materialized_billable_xact_summary;
+ SELECT m.*, COALESCE(c.circ_lib, g.billing_location, r.pickup_lib) AS billing_location
+ FROM money.materialized_billable_xact_summary m
+ LEFT JOIN action.circulation c ON (c.id = m.id)
+ LEFT JOIN money.grocery g ON (g.id = m.id)
+ LEFT JOIN booking.reservation r ON (r.id = m.id);
CREATE TABLE config.marc21_rec_type_map (
code TEXT PRIMARY KEY,
INSERT INTO config.internal_flag (name) VALUES ('ingest.disable_metabib_rec_descriptor');
INSERT INTO config.internal_flag (name) VALUES ('ingest.disable_metabib_field_entry');
INSERT INTO config.internal_flag (name) VALUES ('ingest.disable_authority_linking');
+INSERT INTO config.internal_flag (name) VALUES ('ingest.metarecord_mapping.skip_on_update');
+INSERT INTO config.internal_flag (name) VALUES ('ingest.assume_inserts_only');
CREATE TABLE authority.bib_linking (
id BIGSERIAL PRIMARY KEY,
CREATE OR REPLACE FUNCTION metabib.reingest_metabib_rec_descriptor( bib_id BIGINT ) RETURNS VOID AS $func$
BEGIN
- DELETE FROM metabib.rec_descriptor WHERE record = bib_id;
+ 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' ),
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' ),
- biblio.marc21_extract_fixed_field( bib_id, 'Date1'),
- biblio.marc21_extract_fixed_field( bib_id, 'Date2');
+ 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;
fclass RECORD;
ind_data metabib.field_entry_template%ROWTYPE;
BEGIN
- FOR fclass IN SELECT * FROM config.metabib_class LOOP
- -- RAISE NOTICE 'Emptying out %', fclass.name;
- EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
- END LOOP;
-
- DELETE FROM metabib.facet_entry WHERE source = bib_id;
+ PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+ IF NOT FOUND THEN
+ FOR fclass IN SELECT * FROM config.metabib_class LOOP
+ -- RAISE NOTICE 'Emptying out %', fclass.name;
+ EXECUTE $$DELETE FROM metabib.$$ || fclass.name || $$_field_entry WHERE source = $$ || bib_id;
+ END LOOP;
+ DELETE FROM metabib.facet_entry WHERE source = bib_id;
+ END IF;
FOR ind_data IN SELECT * FROM biblio.extract_metabib_field_entry( bib_id ) LOOP
IF ind_data.field < 0 THEN
CREATE OR REPLACE FUNCTION metabib.reingest_metabib_full_rec( bib_id BIGINT ) RETURNS VOID AS $func$
BEGIN
- DELETE FROM metabib.real_full_rec WHERE record = bib_id;
+ PERFORM * FROM config.internal_flag WHERE name = 'ingest.assume_inserts_only' AND enabled;
+ IF NOT FOUND THEN
+ DELETE FROM metabib.real_full_rec WHERE record = bib_id;
+ END IF;
INSERT INTO metabib.real_full_rec (record, tag, ind1, ind2, subfield, value)
SELECT record, tag, ind1, ind2, subfield, value FROM biblio.flatten_marc( bib_id );
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 metabib.remap_metarecord_for_bib( NEW.id, NEW.fingerprint );
+ 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;
DROP TRIGGER IF EXISTS zzz_update_materialized_simple_rec_delete_tgr ON biblio.record_entry;
-CREATE OR REPLACE FUNCTION oils_xpath_table ( key TEXT, document_field TEXT, relation_name TEXT, xpaths TEXT, criteria TEXT )
-RETURNS SETOF RECORD AS $func$
+CREATE OR REPLACE FUNCTION oils_xpath_table ( key TEXT, document_field TEXT, relation_name TEXT, xpaths TEXT, criteria TEXT ) RETURNS SETOF RECORD AS $func$
DECLARE
xpath_list TEXT[];
select_list TEXT[];
empty_test RECORD;
BEGIN
xpath_list := STRING_TO_ARRAY( xpaths, '|' );
-
+
select_list := ARRAY_APPEND( select_list, key || '::INT AS key' );
-
+
FOR i IN 1 .. ARRAY_UPPER(xpath_list,1) LOOP
- select_list := ARRAY_APPEND(
- select_list,
- $sel$
- EXPLODE_ARRAY(
- COALESCE(
- NULLIF(
- oils_xpath(
- $sel$ ||
- quote_literal(
- CASE
- WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
- ELSE xpath_list[i] || '//text()'
- END
- ) ||
- $sel$,
- $sel$ || document_field || $sel$
+ IF xpath_list[i] = 'null()' THEN
+ select_list := ARRAY_APPEND( select_list, 'NULL::TEXT AS c_' || i );
+ ELSE
+ select_list := ARRAY_APPEND(
+ select_list,
+ $sel$
+ EXPLODE_ARRAY(
+ COALESCE(
+ NULLIF(
+ oils_xpath(
+ $sel$ ||
+ quote_literal(
+ CASE
+ WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
+ ELSE xpath_list[i] || '//text()'
+ END
+ ) ||
+ $sel$,
+ $sel$ || document_field || $sel$
+ ),
+ '{}'::TEXT[]
),
- '{}'::TEXT[]
- ),
- '{NULL}'::TEXT[]
- )
- ) AS c_$sel$ || i
- );
- where_list := ARRAY_APPEND(
- where_list,
- 'c_' || i || ' IS NOT NULL'
- );
+ '{NULL}'::TEXT[]
+ )
+ ) AS c_$sel$ || i
+ );
+ where_list := ARRAY_APPEND(
+ where_list,
+ 'c_' || i || ' IS NOT NULL'
+ );
+ END IF;
END LOOP;
-
+
q := $q$
SELECT * FROM (
SELECT $q$ || ARRAY_TO_STRING( select_list, ', ' ) || $q$ FROM $q$ || relation_name || $q$ WHERE ($q$ || criteria || $q$)
)x WHERE $q$ || ARRAY_TO_STRING( where_list, ' AND ' );
-- RAISE NOTICE 'query: %', q;
-
+
FOR out_record IN EXECUTE q LOOP
RETURN NEXT out_record;
END LOOP;
-
+
RETURN;
END;
-$func$ LANGUAGE PLPGSQL;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
DECLARE
END;
$creator$ LANGUAGE 'plpgsql';
-SELECT acq.create_acq_auditor ( 'acq', 'purchase_order' );
-CREATE INDEX acq_po_hist_id_idx ON acq.acq_purchase_order_history( id );
-
-SELECT acq.create_acq_auditor ( 'acq', 'lineitem' );
-CREATE INDEX acq_lineitem_hist_id_idx ON acq.acq_lineitem_history( id );
+ALTER TABLE acq.lineitem DROP COLUMN item_count;
CREATE OR REPLACE VIEW acq.fund_debit_total AS
SELECT fund.id AS fund,
CREATE TABLE acq.lineitem_alert_text (
id SERIAL PRIMARY KEY,
- code TEXT UNIQUE NOT NULL,
+ code TEXT NOT NULL,
description TEXT,
owning_lib INT NOT NULL
REFERENCES actor.org_unit(id)
fund
) AS c USING ( fund );
-CREATE OR REPLACE FUNCTION actor.usr_merge(
- src_usr INT,
- dest_usr INT,
- del_addrs BOOLEAN,
- del_cards BOOLEAN,
- deactivate_cards BOOLEAN
-) RETURNS VOID AS $$
+CREATE OR REPLACE FUNCTION actor.usr_merge( src_usr INT, dest_usr INT, del_addrs BOOLEAN, del_cards BOOLEAN, deactivate_cards BOOLEAN ) RETURNS VOID AS $$
DECLARE
suffix TEXT;
bucket_row RECORD;
-- acq.*
UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
- UPDATE acq.fund_transfer SET transfer_user = dest_usr WHERE transfer_user = src_usr;
+ UPDATE acq.fund_transfer SET transfer_user = dest_usr WHERE transfer_user = src_usr;
-- transfer picklists the same way we transfer buckets (see above)
FOR picklist_row in
UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
- UPDATE acq.provider_note SET creator = dest_usr WHERE creator = src_usr;
- UPDATE acq.provider_note SET editor = dest_usr WHERE editor = src_usr;
+ UPDATE acq.provider_note SET creator = dest_usr WHERE creator = src_usr;
+ UPDATE acq.provider_note SET editor = dest_usr WHERE editor = src_usr;
UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
UPDATE acq.lineitem_usr_attr_definition SET usr = dest_usr WHERE usr = src_usr;
TRUNCATE money.materialized_billable_xact_summary;
INSERT INTO money.materialized_billable_xact_summary SELECT * FROM money.billable_xact_summary;
+-- Now redefine the view as a window onto the materialized view
+CREATE OR REPLACE VIEW money.billable_xact_summary AS
+ SELECT * FROM money.materialized_billable_xact_summary;
+
CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_nd(
user_id IN INTEGER,
perm_code IN TEXT
$$ LANGUAGE 'plpgsql';
ALTER TABLE acq.purchase_order
- ADD COLUMN cancel_reason INT REFERENCES acq.cancel_reason( id )
- DEFERRABLE INITIALLY DEFERRED;
-
-ALTER TABLE acq.acq_purchase_order_history
- ADD COLUMN cancel_reason INTEGER;
-
-ALTER TABLE acq.purchase_order
+ ADD COLUMN cancel_reason INT
+ REFERENCES acq.cancel_reason( id )
+ DEFERRABLE INITIALLY DEFERRED,
ADD COLUMN prepayment_required BOOLEAN NOT NULL DEFAULT FALSE;
-ALTER TABLE acq.acq_purchase_order_history
- ADD COLUMN prepayment_required BOOLEAN NOT NULL DEFAULT FALSE;
+-- Build the history table and lifecycle view
+-- for acq.purchase_order
-ALTER TABLE acq.lineitem
- ADD COLUMN cancel_reason INT REFERENCES acq.cancel_reason( id )
- DEFERRABLE INITIALLY DEFERRED;
-
-ALTER TABLE acq.acq_lineitem_history
- ADD COLUMN cancel_reason INTEGER;
-
-ALTER TABLE acq.lineitem
- ADD COLUMN estimated_unit_price NUMERIC;
+SELECT acq.create_acq_auditor ( 'acq', 'purchase_order' );
-ALTER TABLE acq.acq_lineitem_history
- ADD COLUMN estimated_unit_price NUMERIC;
+CREATE INDEX acq_po_hist_id_idx ON acq.acq_purchase_order_history( id );
ALTER TABLE acq.lineitem
+ ADD COLUMN cancel_reason INT
+ REFERENCES acq.cancel_reason( id )
+ DEFERRABLE INITIALLY DEFERRED,
+ ADD COLUMN estimated_unit_price NUMERIC,
ADD COLUMN claim_policy INT
REFERENCES acq.claim_policy
- DEFERRABLE INITIALLY DEFERRED;
+ DEFERRABLE INITIALLY DEFERRED,
+ ALTER COLUMN eg_bib_id SET DATA TYPE bigint;
-ALTER TABLE acq.acq_lineitem_history
- ADD COLUMN claim_policy INT
- REFERENCES acq.claim_policy
- DEFERRABLE INITIALLY DEFERRED;
+-- Build the history table and lifecycle view
+-- for acq.lineitem
+
+SELECT acq.create_acq_auditor ( 'acq', 'lineitem' );
+CREATE INDEX acq_lineitem_hist_id_idx ON acq.acq_lineitem_history( id );
ALTER TABLE acq.lineitem_detail
ADD COLUMN cancel_reason INT REFERENCES acq.cancel_reason( id )
END IF;
END IF;
- add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),''),'');
- strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),''),'');
- replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),''),'');
- preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),''),'');
+ add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),','),'');
+ strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),','),'');
+ replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),','),'');
+ preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),','),'');
output.add_rule := BTRIM(add_rule,',');
output.replace_rule := BTRIM(replace_rule,',');
UPDATE config.metabib_field SET label = name;
ALTER TABLE config.metabib_field ALTER COLUMN label SET NOT NULL;
-ALTER TABLE config.metabib_field ADD CONSTRAINT field_class_fkey FOREIGN KEY (field_class) REFERENCES config.metabib_class (name);
+ALTER TABLE config.metabib_field ADD CONSTRAINT metabib_field_field_class_fkey
+ FOREIGN KEY (field_class) REFERENCES config.metabib_class (name);
ALTER TABLE config.metabib_field DROP CONSTRAINT metabib_field_field_class_check;
END;
$func$ LANGUAGE PLPGSQL;
-ALTER TABLE biblio.record_entry ADD COLUMN owner INT REFERENCES actor.org_unit (id);
+ALTER TABLE biblio.record_entry ADD COLUMN owner INT;
+ALTER TABLE biblio.record_entry
+ ADD CONSTRAINT biblio_record_entry_owner_fkey FOREIGN KEY (owner)
+ REFERENCES actor.org_unit (id)
+ DEFERRABLE INITIALLY DEFERRED;
+
ALTER TABLE biblio.record_entry ADD COLUMN share_depth INT;
ALTER TABLE auditor.biblio_record_entry_history ADD COLUMN owner INT;
SELECT * INTO circ_chain_tail FROM action.circ_chain(circ_chain_head.id) ORDER BY xact_start DESC LIMIT 1;
EXIT WHEN circ_chain_tail.xact_finish IS NULL;
- -- Now get the user setings, if any, to block purging if the user wants to keep more circs
+ -- Now get the user settings, if any, to block purging if the user wants to keep more circs
usr_keep_age.value := NULL;
SELECT * INTO usr_keep_age FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_age';
usr_keep_start.value := NULL;
- SELECT * INTO usr_keep_start FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_start_date';
+ SELECT * INTO usr_keep_start FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_start';
IF usr_keep_age.value IS NOT NULL AND usr_keep_start.value IS NOT NULL THEN
- IF oils_json_to_string(usr_keep_age.value)::INTERVAL > AGE(NOW(), oils_json_to_string(usr_keep_start.value)::TIMESTAMPTZ) THEN
- keep_age := AGE(NOW(), oils_json_to_string(usr_keep_start.value)::TIMESTAMPTZ);
+ IF oils_json_to_text(usr_keep_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ) THEN
+ keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
ELSE
- keep_age := oils_json_to_string(usr_keep_age.value)::INTERVAL;
+ keep_age := oils_json_to_text(usr_keep_age.value)::INTERVAL;
END IF;
ELSIF usr_keep_start.value IS NOT NULL THEN
- keep_age := AGE(NOW(), oils_json_to_string(usr_keep_start.value)::TIMESTAMPTZ);
+ keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
ELSE
- keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTEVAL );
+ keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTERVAL );
END IF;
EXIT WHEN AGE(NOW(), circ_chain_tail.xact_finish) < keep_age;
expected_date_offset INTERVAL
-- acquisitions/business-side tables link to here
);
+CREATE INDEX serial_subscription_record_idx ON serial.subscription (record_entry);
+CREATE INDEX serial_subscription_owner_idx ON serial.subscription (owning_lib);
--at least one distribution per org_unit holding issues
CREATE TABLE serial.distribution (
unit_label_prefix TEXT,
unit_label_suffix TEXT
);
+CREATE INDEX serial_distribution_sub_idx ON serial.distribution (subscription);
+CREATE INDEX serial_distribution_holding_lib_idx ON serial.distribution (holding_lib);
CREATE UNIQUE INDEX one_dist_per_sre_idx ON serial.distribution (record_entry);
DEFERRABLE INITIALLY DEFERRED,
routing_label TEXT
);
+CREATE INDEX serial_stream_dist_idx ON serial.stream (distribution);
CREATE UNIQUE INDEX label_once_per_dist
ON serial.stream (distribution, routing_label)
(reader IS NULL AND department IS NOT NULL)
)
);
+CREATE INDEX serial_routing_list_user_stream_idx ON serial.routing_list_user (stream);
+CREATE INDEX serial_routing_list_user_reader_idx ON serial.routing_list_user (reader);
CREATE TABLE serial.caption_and_pattern (
id SERIAL PRIMARY KEY,
chron_4 TEXT,
chron_5 TEXT
);
+CREATE INDEX serial_caption_and_pattern_sub_idx ON serial.caption_and_pattern (subscription);
CREATE TABLE serial.issuance (
id SERIAL PRIMARY KEY,
holding_link_id INT
-- TODO: add columns for separate enumeration/chronology values
);
+CREATE INDEX serial_issuance_sub_idx ON serial.issuance (subscription);
+CREATE INDEX serial_issuance_caption_and_pattern_idx ON serial.issuance (caption_and_pattern);
+CREATE INDEX serial_issuance_date_published_idx ON serial.issuance (date_published);
CREATE TABLE serial.unit (
label TEXT,
label_sort_key TEXT,
contents TEXT NOT NULL
) INHERITS (asset.copy);
+CREATE UNIQUE INDEX unit_barcode_key ON serial.unit (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
+CREATE INDEX unit_cn_idx ON serial.unit (call_number);
+CREATE INDEX unit_avail_cn_idx ON serial.unit (call_number);
+CREATE INDEX unit_creator_idx ON serial.unit ( creator );
+CREATE INDEX unit_editor_idx ON serial.unit ( editor );
ALTER TABLE serial.unit ADD PRIMARY KEY (id);
DEFAULT 'Expected',
shadowed BOOL NOT NULL DEFAULT FALSE
);
+CREATE INDEX serial_item_stream_idx ON serial.item (stream);
+CREATE INDEX serial_item_issuance_idx ON serial.item (issuance);
+CREATE INDEX serial_item_unit_idx ON serial.item (unit);
+CREATE INDEX serial_item_uri_idx ON serial.item (uri);
+CREATE INDEX serial_item_date_received_idx ON serial.item (date_received);
+CREATE INDEX serial_item_status_idx ON serial.item (status);
CREATE TABLE serial.item_note (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
value TEXT NOT NULL
);
+CREATE INDEX serial_item_note_item_idx ON serial.item_note (item);
CREATE TABLE serial.basic_summary (
id SERIAL PRIMARY KEY,
textual_holdings TEXT,
show_generated BOOL NOT NULL DEFAULT TRUE
);
+CREATE INDEX serial_basic_summary_dist_idx ON serial.basic_summary (distribution);
CREATE TABLE serial.supplement_summary (
id SERIAL PRIMARY KEY,
textual_holdings TEXT,
show_generated BOOL NOT NULL DEFAULT TRUE
);
+CREATE INDEX serial_supplement_summary_dist_idx ON serial.supplement_summary (distribution);
CREATE TABLE serial.index_summary (
id SERIAL PRIMARY KEY,
textual_holdings TEXT,
show_generated BOOL NOT NULL DEFAULT TRUE
);
+CREATE INDEX serial_index_summary_dist_idx ON serial.index_summary (distribution);
-- DELETE FROM action_trigger.environment WHERE event_def IN (29,30); DELETE FROM action_trigger.event where event_def IN (29,30); DELETE FROM action_trigger.event_definition WHERE id IN (29,30); DELETE FROM action_trigger.hook WHERE key IN ('money.format.payment_receipt.email','money.format.payment_receipt.print'); DELETE FROM config.upgrade_log WHERE version = '0289'; -- from testing, this sql will remove these events, etc.
SELECT authority.propagate_changes( authority, bib ) FROM authority.bib_linking WHERE authority = $1;
$func$ LANGUAGE SQL;
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( TEXT ) RETURNS SETOF authority.full_rec AS $func$
+
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'UTF-8');
+
+my $xml = shift;
+my $r = MARC::Record->new_from_xml( $xml );
+
+return_next( { tag => 'LDR', value => $r->leader } );
+
+for my $f ( $r->fields ) {
+ if ($f->is_control_field) {
+ return_next({ tag => $f->tag, value => $f->data });
+ } else {
+ for my $s ($f->subfields) {
+ return_next({
+ tag => $f->tag,
+ ind1 => $f->indicator(1),
+ ind2 => $f->indicator(2),
+ subfield => $s->[0],
+ value => $s->[1]
+ });
+
+ }
+ }
+}
+
+return undef;
+
+$func$ LANGUAGE PLPERLU;
+
+CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
+DECLARE
+ auth authority.record_entry%ROWTYPE;
+ output authority.full_rec%ROWTYPE;
+ field RECORD;
+BEGIN
+ SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
+
+ FOR field IN SELECT * FROM authority.flatten_marc( auth.marc ) LOOP
+ output.record := rid;
+ output.ind1 := field.ind1;
+ output.ind2 := field.ind2;
+ output.tag := field.tag;
+ output.subfield := field.subfield;
+ IF field.subfield IS NOT NULL THEN
+ output.value := naco_normalize(field.value, field.subfield);
+ ELSE
+ output.value := field.value;
+ END IF;
+
+ CONTINUE WHEN output.value IS NULL;
+
+ RETURN NEXT output;
+ END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
-- authority.rec_descriptor appears to be unused currently
CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
BEGIN
NEW.marc,
E'(</(?:[^:]*?:)?record>)',
E'<datafield tag="901" ind1=" " ind2=" ">' ||
- '<subfield code="a">' || NEW.arn_value || E'</subfield>' ||
- '<subfield code="b">' || NEW.arn_source || E'</subfield>' ||
'<subfield code="c">' || NEW.id || E'</subfield>' ||
'<subfield code="t">' || TG_TABLE_SCHEMA || E'</subfield>' ||
E'</datafield>\\1'
TRUE
);
+INSERT INTO config.global_flag (name, label, enabled)
+ VALUES (
+ 'circ.holds.usr_not_requestor',
+ oils_i18n_gettext(
+ 'circ.holds.usr_not_requestor',
+ 'Holds: When testing hold matrix matchpoints, use the profile group of the receiving user instead of that of the requestor (affects staff-placed holds)',
+ 'cgf',
+ 'label'
+ ),
+ TRUE
+ );
+
CREATE OR REPLACE FUNCTION maintain_control_numbers() RETURNS TRIGGER AS $func$
use strict;
use MARC::Record;
title TEXT NOT NULL,
value TEXT NOT NULL
);
+CREATE INDEX serial_distribution_note_dist_idx ON serial.distribution_note (distribution);
------- Begin surgery on serial.unit
ADD COLUMN label_sortkey TEXT;
CREATE INDEX asset_call_number_label_sortkey
- ON asset.call_number(label_sortkey);
+ ON asset.call_number(oils_text_as_bytea(label_sortkey));
ALTER TABLE auditor.asset_call_number_history
ADD COLUMN label_class BIGINT;
ALTER TABLE authority.record_entry DROP COLUMN arn_value;
ALTER TABLE authority.record_entry DROP COLUMN arn_source;
-DROP INDEX IF EXISTS authority.unique_by_heading_and_thesaurus;
-
-CREATE INDEX by_heading_and_thesaurus
- ON authority.record_entry (authority.normalize_heading(marc))
- WHERE deleted IS FALSE or deleted = FALSE
-;
-
ALTER TABLE acq.provider_contact
ALTER COLUMN name SET NOT NULL;
CREATE INDEX aud_bib_rec_entry_hist_editor_idx
ON auditor.biblio_record_entry_history ( editor );
+CREATE TABLE action.hold_request_note (
+
+ id BIGSERIAL PRIMARY KEY,
+ hold BIGINT NOT NULL REFERENCES action.hold_request (id)
+ ON DELETE CASCADE
+ DEFERRABLE INITIALLY DEFERRED,
+ title TEXT NOT NULL,
+ body TEXT NOT NULL,
+ slip BOOL NOT NULL DEFAULT FALSE,
+ pub BOOL NOT NULL DEFAULT FALSE,
+ staff BOOL NOT NULL DEFAULT FALSE -- created by staff
+
+);
+CREATE INDEX ahrn_hold_idx ON action.hold_request_note (hold);
+
+-- Tweak a constraint to add a CASCADE
+
+ALTER TABLE action.hold_notification DROP CONSTRAINT hold_notification_hold_fkey;
+
+ALTER TABLE action.hold_notification
+ ADD CONSTRAINT hold_notification_hold_fkey
+ FOREIGN KEY (hold) REFERENCES action.hold_request (id)
+ ON DELETE CASCADE
+ DEFERRABLE INITIALLY DEFERRED;
+
+CREATE TRIGGER asset_label_sortkey_trigger
+ BEFORE UPDATE OR INSERT ON asset.call_number
+ FOR EACH ROW EXECUTE PROCEDURE asset.label_normalizer();
+
+CREATE OR REPLACE FUNCTION container.clear_all_expired_circ_history_items( )
+RETURNS VOID AS $$
+--
+-- Delete expired circulation bucket items for all users that have
+-- a setting for patron.max_reading_list_interval.
+--
+DECLARE
+ today TIMESTAMP WITH TIME ZONE;
+ threshold TIMESTAMP WITH TIME ZONE;
+ usr_setting RECORD;
+BEGIN
+ SELECT date_trunc( 'day', now() ) INTO today;
+ --
+ FOR usr_setting in
+ SELECT
+ usr,
+ value
+ FROM
+ actor.usr_setting
+ WHERE
+ name = 'patron.max_reading_list_interval'
+ LOOP
+ --
+ -- Make sure the setting is a valid interval
+ --
+ BEGIN
+ threshold := today - CAST( translate( usr_setting.value, '"', '' ) AS INTERVAL );
+ EXCEPTION
+ WHEN OTHERS THEN
+ RAISE NOTICE 'Invalid setting patron.max_reading_list_interval for user %: ''%''',
+ usr_setting.usr, usr_setting.value;
+ CONTINUE;
+ END;
+ --
+ --RAISE NOTICE 'User % threshold %', usr_setting.usr, threshold;
+ --
+ DELETE FROM container.copy_bucket_item
+ WHERE
+ bucket IN
+ (
+ SELECT
+ id
+ FROM
+ container.copy_bucket
+ WHERE
+ owner = usr_setting.usr
+ AND btype = 'circ_history'
+ )
+ AND create_time < threshold;
+ END LOOP;
+ --
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION container.clear_all_expired_circ_history_items( ) IS $$
+/*
+ * Delete expired circulation bucket items for all users that have
+ * a setting for patron.max_reading_list_interval.
+*/
+$$;
+
+CREATE OR REPLACE FUNCTION container.clear_expired_circ_history_items(
+ ac_usr IN INTEGER
+) RETURNS VOID AS $$
+--
+-- Delete old circulation bucket items for a specified user.
+-- "Old" means older than the interval specified by a
+-- user-level setting, if it is so specified.
+--
+DECLARE
+ threshold TIMESTAMP WITH TIME ZONE;
+BEGIN
+ -- Sanity check
+ IF ac_usr IS NULL THEN
+ RETURN;
+ END IF;
+ -- Determine the threshold date that defines "old". Subtract the
+ -- interval from the system date, then truncate to midnight.
+ SELECT
+ date_trunc(
+ 'day',
+ now() - CAST( translate( value, '"', '' ) AS INTERVAL )
+ )
+ INTO
+ threshold
+ FROM
+ actor.usr_setting
+ WHERE
+ usr = ac_usr
+ AND name = 'patron.max_reading_list_interval';
+ --
+ IF threshold is null THEN
+ -- No interval defined; don't delete anything
+ -- RAISE NOTICE 'No interval defined for user %', ac_usr;
+ return;
+ END IF;
+ --
+ -- RAISE NOTICE 'Date threshold: %', threshold;
+ --
+ -- Threshold found; do the delete
+ delete from container.copy_bucket_item
+ where
+ bucket in
+ (
+ select
+ id
+ from
+ container.copy_bucket
+ where
+ owner = ac_usr
+ and btype = 'circ_history'
+ )
+ and create_time < threshold;
+ --
+ RETURN;
+END;
+$$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION container.clear_expired_circ_history_items( INTEGER ) IS $$
+/*
+ * Delete old circulation bucket items for a specified user.
+ * "Old" means older than the interval specified by a
+ * user-level setting, if it is so specified.
+*/
+$$;
+
+CREATE OR REPLACE VIEW reporter.hold_request_record AS
+SELECT id,
+ target,
+ hold_type,
+ CASE
+ WHEN hold_type = 'T'
+ THEN target
+ WHEN hold_type = 'I'
+ THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = ahr.target)
+ WHEN hold_type = 'V'
+ THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
+ WHEN hold_type IN ('C','R','F')
+ THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
+ WHEN hold_type = 'M'
+ THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)
+ END AS bib_record
+ FROM action.hold_request ahr;
+
+UPDATE metabib.rec_descriptor
+ SET date1=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date1, ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
+ date2=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date2, ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
+
+-- Change some ints to bigints:
+
+ALTER TABLE container.biblio_record_entry_bucket_item
+ ALTER COLUMN target_biblio_record_entry SET DATA TYPE bigint;
+
+ALTER TABLE vandelay.queued_bib_record
+ ALTER COLUMN imported_as SET DATA TYPE bigint;
+
+ALTER TABLE action.hold_copy_map
+ ALTER COLUMN id SET DATA TYPE bigint;
+
+-- Make due times get pushed to 23:59:59 on insert OR update
+DROP TRIGGER IF EXISTS push_due_date_tgr ON action.circulation;
+CREATE TRIGGER push_due_date_tgr BEFORE INSERT OR UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
+
COMMIT;
-- Some operations go outside of the transaction, because they may
\qecho If any of these CREATE INDEX statements fails because the index already
\qecho exists, ignore the failure.
-CREATE INDEX serial_subscription_record_idx ON serial.subscription (record_entry);
-CREATE INDEX serial_subscription_owner_idx ON serial.subscription (owning_lib);
-CREATE INDEX serial_caption_and_pattern_sub_idx ON serial.caption_and_pattern (subscription);
-CREATE INDEX serial_distribution_sub_idx ON serial.distribution (subscription);
-CREATE INDEX serial_distribution_holding_lib_idx ON serial.distribution (holding_lib);
-CREATE INDEX serial_distribution_note_dist_idx ON serial.distribution_note (distribution);
-CREATE INDEX serial_stream_dist_idx ON serial.stream (distribution);
-CREATE INDEX serial_routing_list_user_stream_idx ON serial.routing_list_user (stream);
-CREATE INDEX serial_routing_list_user_reader_idx ON serial.routing_list_user (reader);
-CREATE INDEX serial_issuance_sub_idx ON serial.issuance (subscription);
-CREATE INDEX serial_issuance_caption_and_pattern_idx ON serial.issuance (caption_and_pattern);
-CREATE INDEX serial_issuance_date_published_idx ON serial.issuance (date_published);
-CREATE UNIQUE INDEX unit_barcode_key ON serial.unit (barcode) WHERE deleted = FALSE OR deleted IS FALSE;
-CREATE INDEX unit_cn_idx ON serial.unit (call_number);
-CREATE INDEX unit_avail_cn_idx ON serial.unit (call_number);
-CREATE INDEX unit_creator_idx ON serial.unit ( creator );
-CREATE INDEX unit_editor_idx ON serial.unit ( editor );
-CREATE INDEX serial_item_stream_idx ON serial.item (stream);
-CREATE INDEX serial_item_issuance_idx ON serial.item (issuance);
-CREATE INDEX serial_item_unit_idx ON serial.item (unit);
-CREATE INDEX serial_item_uri_idx ON serial.item (uri);
-CREATE INDEX serial_item_date_received_idx ON serial.item (date_received);
-CREATE INDEX serial_item_status_idx ON serial.item (status);
-CREATE INDEX serial_item_note_item_idx ON serial.item_note (item);
-CREATE INDEX serial_basic_summary_dist_idx ON serial.basic_summary (distribution);
-CREATE INDEX serial_supplement_summary_dist_idx ON serial.supplement_summary (distribution);
-CREATE INDEX serial_index_summary_dist_idx ON serial.index_summary (distribution);
+CREATE INDEX acq_picklist_owner_idx ON acq.picklist ( owner );
+CREATE INDEX acq_picklist_creator_idx ON acq.picklist ( creator );
+CREATE INDEX acq_picklist_editor_idx ON acq.picklist ( editor );
+CREATE INDEX acq_po_note_creator_idx ON acq.po_note ( creator );
+CREATE INDEX acq_po_note_editor_idx ON acq.po_note ( editor );
+CREATE INDEX fund_alloc_allocator_idx ON acq.fund_allocation ( allocator );
+CREATE INDEX li_creator_idx ON acq.lineitem ( creator );
+CREATE INDEX li_editor_idx ON acq.lineitem ( editor );
+CREATE INDEX li_selector_idx ON acq.lineitem ( selector );
+CREATE INDEX li_note_creator_idx ON acq.lineitem_note ( creator );
+CREATE INDEX li_note_editor_idx ON acq.lineitem_note ( editor );
+CREATE INDEX li_usr_attr_def_usr_idx ON acq.lineitem_usr_attr_definition ( usr );
+CREATE INDEX po_editor_idx ON acq.purchase_order ( editor );
+CREATE INDEX po_creator_idx ON acq.purchase_order ( creator );
+CREATE INDEX acq_po_org_name_order_date_idx ON acq.purchase_order( ordering_agency, name, order_date );
+CREATE INDEX action_in_house_use_staff_idx ON action.in_house_use ( staff );
+CREATE INDEX action_non_cat_circ_patron_idx ON action.non_cataloged_circulation ( patron );
+CREATE INDEX action_non_cat_circ_staff_idx ON action.non_cataloged_circulation ( staff );
+CREATE INDEX action_survey_response_usr_idx ON action.survey_response ( usr );
+CREATE INDEX ahn_notify_staff_idx ON action.hold_notification ( notify_staff );
+CREATE INDEX circ_all_usr_idx ON action.circulation ( usr );
+CREATE INDEX circ_circ_staff_idx ON action.circulation ( circ_staff );
+CREATE INDEX circ_checkin_staff_idx ON action.circulation ( checkin_staff );
+CREATE INDEX hold_request_fulfillment_staff_idx ON action.hold_request ( fulfillment_staff );
+CREATE INDEX hold_request_requestor_idx ON action.hold_request ( requestor );
+CREATE INDEX non_cat_in_house_use_staff_idx ON action.non_cat_in_house_use ( staff );
+CREATE INDEX actor_usr_note_creator_idx ON actor.usr_note ( creator );
+CREATE INDEX actor_usr_standing_penalty_staff_idx ON actor.usr_standing_penalty ( staff );
+CREATE INDEX usr_org_unit_opt_in_staff_idx ON actor.usr_org_unit_opt_in ( staff );
+CREATE INDEX asset_call_number_note_creator_idx ON asset.call_number_note ( creator );
+CREATE INDEX asset_copy_note_creator_idx ON asset.copy_note ( creator );
+CREATE INDEX cp_creator_idx ON asset.copy ( creator );
+CREATE INDEX cp_editor_idx ON asset.copy ( editor );
CREATE INDEX actor_card_barcode_lower_idx ON actor.card (lower(barcode));
-\qecho if the following CREATE INDEX fails, It will be necessary to do some
+DROP INDEX IF EXISTS authority.unique_by_heading_and_thesaurus;
+
+\qecho If the following CREATE INDEX fails, It will be necessary to do some
\qecho data cleanup as described in the comments.
+CREATE UNIQUE INDEX unique_by_heading_and_thesaurus
+ ON authority.record_entry (authority.normalize_heading(marc))
+ WHERE deleted IS FALSE or deleted = FALSE;
+
-- If the unique index fails, uncomment the following to create
-- a regular index that will help find the duplicates in a hurry:
--CREATE INDEX by_heading_and_thesaurus
CREATE TABLE config.hold_matrix_matchpoint (
id SERIAL PRIMARY KEY,
active BOOL NOT NULL DEFAULT TRUE,
+ strict_ou_match BOOL NOT NULL DEFAULT FALSE,
user_home_ou INT 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"
request_ou INT 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"
pickup_ou INT 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"
CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
DECLARE
current_requestor_group permission.grp_tree%ROWTYPE;
- root_ou actor.org_unit%ROWTYPE;
requestor_object actor.usr%ROWTYPE;
user_object actor.usr%ROWTYPE;
item_object asset.copy%ROWTYPE;
current_mp config.hold_matrix_matchpoint%ROWTYPE;
matchpoint config.hold_matrix_matchpoint%ROWTYPE;
BEGIN
- SELECT INTO root_ou * FROM actor.org_unit WHERE parent_ou IS NULL;
SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
CASE WHEN m.marc_vr_format IS NOT NULL THEN 2 ELSE 0 END +
CASE WHEN m.ref_flag IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
- current_mp_weight := 5.0;
+ IF NOT current_mp.strict_ou_match THEN
+ current_mp_weight := 5.0;
+ ELSE
+ current_mp_weight := 0.0;
+ END IF;
IF current_mp.circ_modifier IS NOT NULL THEN
CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
-- caclulate the rule match weight
- IF current_mp.item_owning_ou IS NOT NULL AND current_mp.item_owning_ou <> root_ou.id THEN
- SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+ IF current_mp.item_owning_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.item_owning_ou = item_cn_object.owning_lib THEN 1.0 ELSE 0.0 END;
+ END IF;
current_mp_weight := current_mp_weight - tmp_weight;
END IF;
- IF current_mp.item_circ_ou IS NOT NULL AND current_mp.item_circ_ou <> root_ou.id THEN
- SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+ IF current_mp.item_circ_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.item_circ_ou = item_object.circ_lib THEN 1.0 ELSE 0.0 END;
+ END IF;
current_mp_weight := current_mp_weight - tmp_weight;
END IF;
- IF current_mp.pickup_ou IS NOT NULL AND current_mp.pickup_ou <> root_ou.id THEN
- SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+ IF current_mp.pickup_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.pickup_ou = pickiup_ou THEN 1.0 ELSE 0.0 END;
+ END IF;
current_mp_weight := current_mp_weight - tmp_weight;
END IF;
- IF current_mp.request_ou IS NOT NULL AND current_mp.request_ou <> root_ou.id THEN
- SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+ IF current_mp.request_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.request_ou = request_ou THEN 1.0 ELSE 0.0 END;
+ END IF;
current_mp_weight := current_mp_weight - tmp_weight;
END IF;
- IF current_mp.user_home_ou IS NOT NULL AND current_mp.user_home_ou <> root_ou.id THEN
- SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+ IF current_mp.user_home_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.user_home_ou = user_object.home_ou THEN 1.0 ELSE 0.0 END;
+ END IF;
current_mp_weight := current_mp_weight - tmp_weight;
END IF;
$func$ LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
DECLARE
matchpoint_id INT;
user_object actor.usr%ROWTYPE;
END LOOP;
END IF;
- IF hold_test.max_holds IS NOT NULL THEN
+ IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
SELECT INTO hold_count COUNT(*)
FROM action.hold_request
WHERE usr = match_user
END;
$func$ LANGUAGE plpgsql;
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, FALSE );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION action.hold_retarget_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, TRUE );
+$func$ LANGUAGE SQL;
+
COMMIT;
create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
marc TEXT NOT NULL,
- eg_bib_id INT REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
+ eg_bib_id BIGINT REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
source_label TEXT,
state TEXT NOT NULL DEFAULT 'new',
cancel_reason INT REFERENCES acq.cancel_reason( id )
,(399, 'ADMIN_SERIAL_DISTRIBUTION', oils_i18n_gettext(399, 'Create/update/delete serial distribution objects', 'ppl', 'description'))
,(400, 'ADMIN_SERIAL_STREAM', oils_i18n_gettext(400, 'Create/update/delete serial stream objects', 'ppl', 'description'))
,(401, 'RECEIVE_SERIAL', oils_i18n_gettext(401, 'Receive serial items', 'ppl', 'description'))
+ ,(402, 'ADMIN_ACQ_DISTRIB_FORMULA', oils_i18n_gettext(402, 'Create/update/delete distribution formulae', 'ppl', 'description'))
+ ,(403, 'ADMIN_ACQ_CLAIM', oils_i18n_gettext(403, 'Create/update/delete acquisitions claims', 'ppl', 'description'))
+ ,(404, 'ADMIN_ACQ_CLAIM_EVENT_TYPE', oils_i18n_gettext(404, 'Create/update/delete acquisitions claim event types', 'ppl', 'description'))
+ ,(405, 'ADMIN_ACQ_CLAIM_TYPE', oils_i18n_gettext(405, 'Create/update/delete acquisitions claim types', 'ppl', 'description'))
+ ,(406, 'ADMIN_ACQ_FISCAL_YEAR', oils_i18n_gettext(406, 'Create/update/delete acquisitions fiscal years', 'ppl', 'description'))
+ ,(407, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT', oils_i18n_gettext(407, 'Create/update/delete acquisitions fund allocation percentages', 'ppl', 'description'))
+ ,(408, 'ADMIN_ACQ_FUND_TAG', oils_i18n_gettext(408, 'Create/update/delete acquisitions fund tags', 'ppl', 'description'))
+ ,(409, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT', oils_i18n_gettext(409, 'Create/update/delete acquisitions lineitem alert text', 'ppl', 'description'))
+
;
INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('staff_client', oils_i18n_gettext('staff_client', 'General Staff Client container', 'cbrebt', 'label'));
INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('bookbag', oils_i18n_gettext('bookbag', 'Book Bag', 'cbrebt', 'label'));
INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('reading_list', oils_i18n_gettext('reading_list', 'Reading List', 'cbrebt', 'label'));
+INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('template_merge',oils_i18n_gettext('template_merge','Template Merge Container', 'cbrebt', 'label'));
INSERT INTO container.user_bucket_type (code,label) VALUES ('misc', oils_i18n_gettext('misc', 'Miscellaneous', 'cubt', 'label'));
INSERT INTO container.user_bucket_type (code,label) VALUES ('folks', oils_i18n_gettext('folks', 'Friends', 'cubt', 'label'));
<tbody>
[% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
[%
- IF copy.eg_copy_id;
- SET copy = copy.eg_copy_id;
+ IF detail.eg_copy_id;
+ SET copy = detail.eg_copy_id;
SET cn_label = copy.call_number.label;
ELSE;
SET copy = detail;
INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, cleanup_success, cleanup_failure, delay, delay_field, group_field, template)
- VALUES (23, true, 1, 'PO JEDI', 'acqpo.activated', 'Acq::PurchaseOrderEDIRequired', 'GeneratePurchaseOrderJEDI', NULL, NULL, '00:05:00', NULL, NULL,
+ VALUES (23, true, 1, 'PO JEDI', 'acqpo.activated', 'Acq::PurchaseOrderEDIRequired', 'GeneratePurchaseOrderJEDI', NULL, NULL, '00:00:00', NULL, NULL,
$$[%- USE date -%]
-[%# start JEDI document -%]
+[%# start JEDI document
+ # Vendor specific kludges:
+ # BT - vendcode goes to NAD/BY *suffix* w/ 91 qualifier
+ # INGRAM - vendcode goes to NAD/BY *segment* w/ 91 qualifier (separately)
+ # BRODART - vendcode goes to FTX segment (lineitem level)
+-%]
+[%-
+IF target.provider.edi_default.vendcode && target.provider.code == 'BRODART';
+ xtra_ftx = target.provider.edi_default.vendcode;
+END;
+-%]
[%- BLOCK big_block -%]
{
"recipient":"[% target.provider.san %]",
"ORDERS":[ "order", {
"po_number":[% target.id %],
"date":"[% date.format(date.now, '%Y%m%d') %]",
- "buyer":[{
- [%- IF target.provider.edi_default.vendcode -%]
- "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]",
- "id-qualifier": 91
+ "buyer":[
+ [% IF target.provider.edi_default.vendcode && (target.provider.code == 'BT' || target.provider.name.match('(?i)^BAKER & TAYLOR')) -%]
+ {"id-qualifier": 91, "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]"}
+ [%- ELSIF target.provider.edi_default.vendcode && target.provider.code == 'INGRAM' -%]
+ {"id":"[% target.ordering_agency.mailing_address.san %]"},
+ {"id-qualifier": 91, "id":"[% target.provider.edi_default.vendcode %]"}
[%- ELSE -%]
- "id":"[% target.ordering_agency.mailing_address.san %]"
- [%- END -%]
- }],
- "vendor":[
+ {"id":"[% target.ordering_agency.mailing_address.san %]"}
+ [%- END -%]
+ ],
+ "vendor":[
[%- # target.provider.name (target.provider.id) -%]
"[% target.provider.san %]",
{"id-qualifier": 92, "id":"[% target.provider.id %]"}
],
"currency":"[% target.provider.currency_type %]",
+
"items":[
- [% FOR li IN target.lineitems %]
+ [%- FOR li IN target.lineitems %]
{
+ "line_index":"[% li.id %]",
"identifiers":[ [%-# li.isbns = helpers.get_li_isbns(li.attributes) %]
[% FOR isbn IN helpers.get_li_isbns(li.attributes) -%]
[% IF isbn.length == 13 -%]
{"id-qualifier":"IB","id":"[% isbn %]"},
[%- END %]
[% END %]
- {"id-qualifier":"SA","id":"[% li.id %]"}
+ {"id-qualifier":"IN","id":"[% li.id %]"}
],
"price":[% li.estimated_unit_price || '0.00' %],
"desc":[
- {"BTI":"[% helpers.get_li_attr('title', '', li.attributes) %]"},
+ {"BTI":"[% helpers.get_li_attr('title', '', li.attributes) %]"},
{"BPU":"[% helpers.get_li_attr('publisher', '', li.attributes) %]"},
{"BPD":"[% helpers.get_li_attr('pubdate', '', li.attributes) %]"},
{"BPH":"[% helpers.get_li_attr('pagination','', li.attributes) %]"}
],
+ [%- ftx_vals = [];
+ FOR note IN li.lineitem_notes;
+ NEXT UNLESS note.vendor_public == 't';
+ ftx_vals.push(note.value);
+ END;
+ IF xtra_ftx; ftx_vals.unshift(xtra_ftx); END;
+ IF ftx_vals.size == 0; ftx_vals.unshift(''); END; # BT needs FTX+LIN for every LI, even if it is an empty one
+ -%]
+
+ "free-text":[
+ [% FOR note IN ftx_vals -%] "[% note %]"[% UNLESS loop.last %], [% END %][% END %]
+ ],
"quantity":[% li.lineitem_details.size %]
}[% UNLESS loop.last %],[% END %]
[%-# TODO: lineitem details (later) -%]
[% END %]
],
"line_items":[% target.lineitems.size %]
- }] [% # close ORDERS array %]
- }] [% # close body array %]
+ }] [%# close ORDERS array %]
+ }] [%# close body array %]
}
[% END %]
[% tempo = PROCESS big_block; helpers.escape_json(tempo) %]
CASE
WHEN hold_type = 'T'
THEN target
+ WHEN hold_type = 'I'
+ THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = ahr.target)
WHEN hold_type = 'V'
THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
- WHEN hold_type = 'C'
+ WHEN hold_type IN ('C','R','F')
THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
WHEN hold_type = 'M'
THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)
--- /dev/null
+BEGIN;
+
+-- Adding a trigger. Upgrade # 0364 created the trigger function but not
+-- the trigger itself. However the base install script 040.schema.asset.sql
+-- creates both the function and the trigger.
+
+INSERT INTO config.upgrade_log (version) VALUES ('0414'); -- Scott McKellar
+
+CREATE TRIGGER asset_label_sortkey_trigger
+ BEFORE UPDATE OR INSERT ON asset.call_number
+ FOR EACH ROW EXECUTE PROCEDURE asset.label_normalizer();
+
+COMMIT;
--- /dev/null
+-- Dropping and recreating a foreign key constraint for config.metabib_field,
+-- in order to change its name. WHen this foreign key was first introduced,
+-- the upgrade script gave it one name and the base install script gave it
+-- a different name. Here we bring the names into sync.
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0415'); -- Scott McKellar
+
+\qecho Dropping and recreating a foreign key in order to change its name.
+\qecho If the DROP fails because the constraint doesn't exist under the old
+\qecho name, or the ADD fails because it already exists under the new name,
+\qecho then ignore the failure.
+
+ALTER TABLE config.metabib_field
+ DROP CONSTRAINT field_class_fkey;
+
+ALTER TABLE config.metabib_field
+ ADD CONSTRAINT metabib_field_field_class_fkey
+ FOREIGN KEY (field_class) REFERENCES config.metabib_class(name);
+
+COMMIT;
--- /dev/null
+INSERT INTO config.upgrade_log (version) VALUES ('0416'); -- Scott McKellar
+
+\qecho No transaction. Renaming two indexes to correct spelling.
+\qecho If either change fails, then the index was probably created
+\qecho correctly in the first place; ignore the failure.
+
+ALTER INDEX config.rule_recuring_fine_name_key
+ RENAME TO rule_recurring_fine_name_key;
+
+ALTER INDEX config.rule_recuring_fine_pkey
+ RENAME TO rule_recurring_fine_pkey;
--- /dev/null
+-- Drop the never-used column item_count from acq.lineitem.
+-- Drop it also from the associated history table, and rebuild
+-- the function that maintains it. Finally, rebuild the
+-- associated lifecycle view.
+
+-- Apply to trunk only; this column never existed in 2.0.
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0417'); -- Scott McKellar
+
+-- Have to drop the view first, because it's a dependent
+DROP VIEW IF EXISTS acq.acq_lineitem_lifecycle;
+
+ALTER TABLE acq.lineitem DROP COLUMN item_count;
+
+ALTER TABLE acq.acq_lineitem_history DROP COLUMN item_count;
+
+SELECT acq.create_acq_func( 'acq', 'lineitem' );
+
+SELECT acq.create_acq_lifecycle( 'acq', 'lineitem' );
+
+COMMIT;
--- /dev/null
+-- Apply in an update script some fixes that were previously applied only
+-- to the base installation script 090.schema.action.sql.
+
+-- Also fix a typo: INTEVAL -> INTERVAL
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0418'); -- Scott McKellar
+
+CREATE OR REPLACE FUNCTION action.purge_circulations () RETURNS INT AS $func$
+DECLARE
+ usr_keep_age actor.usr_setting%ROWTYPE;
+ usr_keep_start actor.usr_setting%ROWTYPE;
+ org_keep_age INTERVAL;
+ org_keep_count INT;
+
+ keep_age INTERVAL;
+
+ target_acp RECORD;
+ circ_chain_head action.circulation%ROWTYPE;
+ circ_chain_tail action.circulation%ROWTYPE;
+
+ purge_position INT;
+ count_purged INT;
+BEGIN
+
+ count_purged := 0;
+
+ SELECT value::INTERVAL INTO org_keep_age FROM config.global_flag WHERE name = 'history.circ.retention_age' AND enabled;
+
+ SELECT value::INT INTO org_keep_count FROM config.global_flag WHERE name = 'history.circ.retention_count' AND enabled;
+ IF org_keep_count IS NULL THEN
+ RETURN count_purged; -- Gimme a count to keep, or I keep them all, forever
+ END IF;
+
+ -- First, find copies with more than keep_count non-renewal circs
+ FOR target_acp IN
+ SELECT target_copy,
+ COUNT(*) AS total_real_circs
+ FROM action.circulation
+ WHERE parent_circ IS NULL
+ AND xact_finish IS NOT NULL
+ GROUP BY target_copy
+ HAVING COUNT(*) > org_keep_count
+ LOOP
+ purge_position := 0;
+ -- And, for those, select circs that are finished and older than keep_age
+ FOR circ_chain_head IN
+ SELECT *
+ FROM action.circulation
+ WHERE target_copy = target_acp.target_copy
+ AND parent_circ IS NULL
+ ORDER BY xact_start
+ LOOP
+
+ -- Stop once we've purged enough circs to hit org_keep_count
+ EXIT WHEN target_acp.total_real_circs - purge_position <= org_keep_count;
+
+ SELECT * INTO circ_chain_tail FROM action.circ_chain(circ_chain_head.id) ORDER BY xact_start DESC LIMIT 1;
+ EXIT WHEN circ_chain_tail.xact_finish IS NULL;
+
+ -- Now get the user settings, if any, to block purging if the user wants to keep more circs
+ usr_keep_age.value := NULL;
+ SELECT * INTO usr_keep_age FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_age';
+
+ usr_keep_start.value := NULL;
+ SELECT * INTO usr_keep_start FROM actor.usr_setting WHERE usr = circ_chain_head.usr AND name = 'history.circ.retention_start';
+
+ IF usr_keep_age.value IS NOT NULL AND usr_keep_start.value IS NOT NULL THEN
+ IF oils_json_to_text(usr_keep_age.value)::INTERVAL > AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ) THEN
+ keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
+ ELSE
+ keep_age := oils_json_to_text(usr_keep_age.value)::INTERVAL;
+ END IF;
+ ELSIF usr_keep_start.value IS NOT NULL THEN
+ keep_age := AGE(NOW(), oils_json_to_text(usr_keep_start.value)::TIMESTAMPTZ);
+ ELSE
+ keep_age := COALESCE( org_keep_age::INTERVAL, '2000 years'::INTERVAL );
+ END IF;
+
+ EXIT WHEN AGE(NOW(), circ_chain_tail.xact_finish) < keep_age;
+
+ -- We've passed the purging tests, purge the circ chain starting at the end
+ DELETE FROM action.circulation WHERE id = circ_chain_tail.id;
+ WHILE circ_chain_tail.parent_circ IS NOT NULL LOOP
+ SELECT * INTO circ_chain_tail FROM action.circulation WHERE id = circ_chain_tail.parent_circ;
+ DELETE FROM action.circulation WHERE id = circ_chain_tail.id;
+ END LOOP;
+
+ count_purged := count_purged + 1;
+ purge_position := purge_position + 1;
+
+ END LOOP;
+ END LOOP;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0419'); -- miker
+
+CREATE OR REPLACE VIEW reporter.hold_request_record AS
+SELECT id,
+ target,
+ hold_type,
+ CASE
+ WHEN hold_type = 'T'
+ THEN target
+ WHEN hold_type = 'I'
+ THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = ahr.target)
+ WHEN hold_type = 'V'
+ THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
+ WHEN hold_type IN ('C','R','F')
+ THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
+ WHEN hold_type = 'M'
+ THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)
+ END AS bib_record
+ FROM action.hold_request ahr;
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0420'); -- miker
+
+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;
+
+UPDATE metabib.rec_descriptor
+ SET date1=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date1, ''), E'\\D', '0', 'g')::INT,0)::TEXT,4,'0'),
+ date2=LPAD(NULLIF(REGEXP_REPLACE(NULLIF(date2, ''), E'\\D', '9', 'g')::INT,9999)::TEXT,4,'0');
+
+COMMIT;
--- /dev/null
+-- 1. Turn some ints into bigints.
+
+-- 2. Rename a constraint for consistency and accuracy (currently it may
+-- have either of two different names).
+
+\qecho One of the following DROPs will fail, so we do them
+\qecho both outside of a transaction. Ignore the failure.
+
+ALTER TABLE booking.resource_type
+ DROP CONSTRAINT brt_name_or_record_once_per_owner;
+
+ALTER TABLE booking.resource_type
+ DROP CONSTRAINT brt_name_once_per_owner;
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0421'); -- Scott McKellar
+
+ALTER TABLE booking.resource_type
+ ALTER COLUMN record SET DATA TYPE bigint,
+ ADD CONSTRAINT brt_name_and_record_once_per_owner UNIQUE(owner, name, record);
+
+ALTER TABLE container.biblio_record_entry_bucket_item
+ ALTER COLUMN target_biblio_record_entry SET DATA TYPE bigint;
+
+-- Before we can embiggen the next one, we must drop a view
+-- that depends on it (and recreate it later)
+
+DROP VIEW IF EXISTS acq.acq_lineitem_lifecycle;
+
+ALTER TABLE acq.lineitem
+ ALTER COLUMN eg_bib_id SET DATA TYPE bigint;
+
+-- Recreate the view
+
+SELECT acq.create_acq_lifecycle( 'acq', 'lineitem' );
+
+ALTER TABLE vandelay.queued_bib_record
+ ALTER COLUMN imported_as SET DATA TYPE bigint;
+
+ALTER TABLE action.hold_copy_map
+ ALTER COLUMN id SET DATA TYPE bigint;
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+-- Turn an int into a bigint in acq.acq_lineitem_history, following up on
+-- a similar change to acq.lineitem
+
+INSERT INTO config.upgrade_log (version) VALUES ('0422'); -- Scott McKellar
+
+DROP VIEW IF EXISTS acq.acq_lineitem_lifecycle;
+
+ALTER TABLE acq.acq_lineitem_history
+ ALTER COLUMN eg_bib_id SET DATA TYPE bigint;
+
+SELECT acq.create_acq_lifecycle( 'acq', 'lineitem' );
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0423'); --miker
+
+CREATE OR REPLACE FUNCTION oils_xpath_table ( key TEXT, document_field TEXT, relation_name TEXT, xpaths TEXT, criteria TEXT ) RETURNS SETOF RECORD AS $func$
+DECLARE
+ xpath_list TEXT[];
+ select_list TEXT[];
+ where_list TEXT[];
+ q TEXT;
+ out_record RECORD;
+ empty_test RECORD;
+BEGIN
+ xpath_list := STRING_TO_ARRAY( xpaths, '|' );
+
+ select_list := ARRAY_APPEND( select_list, key || '::INT AS key' );
+
+ FOR i IN 1 .. ARRAY_UPPER(xpath_list,1) LOOP
+ IF xpath_list[i] = 'null()' THEN
+ select_list := ARRAY_APPEND( select_list, 'NULL::TEXT AS c_' || i );
+ ELSE
+ select_list := ARRAY_APPEND(
+ select_list,
+ $sel$
+ EXPLODE_ARRAY(
+ COALESCE(
+ NULLIF(
+ oils_xpath(
+ $sel$ ||
+ quote_literal(
+ CASE
+ WHEN xpath_list[i] ~ $re$/[^/[]*@[^/]+$$re$ OR xpath_list[i] ~ $re$text\(\)$$re$ THEN xpath_list[i]
+ ELSE xpath_list[i] || '//text()'
+ END
+ ) ||
+ $sel$,
+ $sel$ || document_field || $sel$
+ ),
+ '{}'::TEXT[]
+ ),
+ '{NULL}'::TEXT[]
+ )
+ ) AS c_$sel$ || i
+ );
+ where_list := ARRAY_APPEND(
+ where_list,
+ 'c_' || i || ' IS NOT NULL'
+ );
+ END IF;
+ END LOOP;
+
+ q := $q$
+SELECT * FROM (
+ SELECT $q$ || ARRAY_TO_STRING( select_list, ', ' ) || $q$ FROM $q$ || relation_name || $q$ WHERE ($q$ || criteria || $q$)
+)x WHERE $q$ || ARRAY_TO_STRING( where_list, ' AND ' );
+ -- RAISE NOTICE 'query: %', q;
+
+ FOR out_record IN EXECUTE q LOOP
+ RETURN NEXT out_record;
+ END LOOP;
+
+ RETURN;
+END;
+$func$ LANGUAGE PLPGSQL IMMUTABLE;
+
+COMMIT;
+
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0424'); -- dbs
+
+DROP TRIGGER push_due_date_tgr ON action.circulation;
+
+CREATE TRIGGER push_due_date_tgr BEFORE INSERT OR UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE action.push_circ_due_time();
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0425'); -- Scott McKellar
+
+ALTER TABLE permission.grp_tree
+ ADD COLUMN hold_priority INT NOT NULL DEFAULT 0;
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0426'); -- senator
+
+INSERT INTO permission.perm_list VALUES
+ (402, 'ADMIN_ACQ_DISTRIB_FORMULA', oils_i18n_gettext(402, 'Create/update/delete distribution formulae', 'ppl', 'description'))
+ ,(403, 'ADMIN_ACQ_CLAIM', oils_i18n_gettext(403, 'Create/update/delete acquisitions claims', 'ppl', 'description'))
+ ,(404, 'ADMIN_ACQ_CLAIM_EVENT_TYPE', oils_i18n_gettext(404, 'Create/update/delete acquisitions claim event types', 'ppl', 'description'))
+ ,(405, 'ADMIN_ACQ_CLAIM_TYPE', oils_i18n_gettext(405, 'Create/update/delete acquisitions claim types', 'ppl', 'description'))
+ ,(406, 'ADMIN_ACQ_FISCAL_YEAR', oils_i18n_gettext(406, 'Create/update/delete acquisitions fiscal years', 'ppl', 'description'))
+ ,(407, 'ADMIN_ACQ_FUND_ALLOCATION_PERCENT', oils_i18n_gettext(407, 'Create/update/delete acquisitions fund allocation percentages', 'ppl', 'description'))
+ ,(408, 'ADMIN_ACQ_FUND_TAG', oils_i18n_gettext(408, 'Create/update/delete acquisitions fund tags', 'ppl', 'description'))
+ ,(409, 'ADMIN_ACQ_LINEITEM_ALERT_TEXT', oils_i18n_gettext(409, 'Create/update/delete acquisitions lineitem alert text', 'ppl', 'description'))
+;
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0427'); -- gmc
+
+CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
+DECLARE
+ current_requestor_group permission.grp_tree%ROWTYPE;
+ requestor_object actor.usr%ROWTYPE;
+ user_object actor.usr%ROWTYPE;
+ item_object asset.copy%ROWTYPE;
+ item_cn_object asset.call_number%ROWTYPE;
+ rec_descriptor metabib.rec_descriptor%ROWTYPE;
+ current_mp_weight FLOAT;
+ matchpoint_weight FLOAT;
+ tmp_weight FLOAT;
+ current_mp config.hold_matrix_matchpoint%ROWTYPE;
+ matchpoint config.hold_matrix_matchpoint%ROWTYPE;
+BEGIN
+ SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+ SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
+ SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+ SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+ SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r WHERE r.record = item_cn_object.record;
+
+ PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
+
+ IF NOT FOUND THEN
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = requestor_object.profile;
+ ELSE
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = user_object.profile;
+ END IF;
+
+ LOOP
+ -- for each potential matchpoint for this ou and group ...
+ FOR current_mp IN
+ SELECT m.*
+ FROM config.hold_matrix_matchpoint m
+ WHERE m.requestor_grp = current_requestor_group.id AND m.active
+ ORDER BY CASE WHEN m.circ_modifier IS NOT NULL THEN 16 ELSE 0 END +
+ CASE WHEN m.juvenile_flag IS NOT NULL THEN 16 ELSE 0 END +
+ CASE WHEN m.marc_type IS NOT NULL THEN 8 ELSE 0 END +
+ CASE WHEN m.marc_form IS NOT NULL THEN 4 ELSE 0 END +
+ CASE WHEN m.marc_vr_format IS NOT NULL THEN 2 ELSE 0 END +
+ CASE WHEN m.ref_flag IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
+
+ current_mp_weight := 5.0;
+
+ IF current_mp.circ_modifier IS NOT NULL THEN
+ CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
+ END IF;
+
+ IF current_mp.marc_type IS NOT NULL THEN
+ IF item_object.circ_as_type IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
+ ELSE
+ CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
+ END IF;
+ END IF;
+
+ IF current_mp.marc_form IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
+ END IF;
+
+ IF current_mp.marc_vr_format IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
+ END IF;
+
+ IF current_mp.juvenile_flag IS NOT NULL THEN
+ CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
+ END IF;
+
+ IF current_mp.ref_flag IS NOT NULL THEN
+ CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
+ END IF;
+
+
+ -- caclulate the rule match weight
+ IF current_mp.item_owning_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.item_circ_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.pickup_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.request_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.user_home_ou IS NOT NULL THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ -- set the matchpoint if we found the best one
+ IF matchpoint_weight IS NULL OR matchpoint_weight > current_mp_weight THEN
+ matchpoint = current_mp;
+ matchpoint_weight = current_mp_weight;
+ END IF;
+
+ END LOOP;
+
+ EXIT WHEN current_requestor_group.parent IS NULL OR matchpoint.id IS NOT NULL;
+
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = current_requestor_group.parent;
+ END LOOP;
+
+ RETURN matchpoint.id;
+END;
+$func$ LANGUAGE plpgsql;
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0428'); -- miker
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
+DECLARE
+ matchpoint_id INT;
+ user_object actor.usr%ROWTYPE;
+ age_protect_object config.rule_age_hold_protect%ROWTYPE;
+ standing_penalty config.standing_penalty%ROWTYPE;
+ transit_range_ou_type actor.org_unit_type%ROWTYPE;
+ transit_source actor.org_unit%ROWTYPE;
+ item_object asset.copy%ROWTYPE;
+ ou_skip actor.org_unit_setting%ROWTYPE;
+ result action.matrix_test_result;
+ hold_test config.hold_matrix_matchpoint%ROWTYPE;
+ hold_count INT;
+ hold_transit_prox INT;
+ frozen_hold_count INT;
+ context_org_list INT[];
+ done BOOL := FALSE;
+BEGIN
+ SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+ SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
+
+ result.success := TRUE;
+
+ -- Fail if we couldn't find a user
+ IF user_object.id IS NULL THEN
+ result.fail_part := 'no_user';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+
+ -- Fail if we couldn't find a copy
+ IF item_object.id IS NULL THEN
+ result.fail_part := 'no_item';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
+ result.matchpoint := matchpoint_id;
+
+ SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
+
+ -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
+ IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
+ result.fail_part := 'circ.holds.target_skip_me';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ -- Fail if user is barred
+ IF user_object.barred IS TRUE THEN
+ result.fail_part := 'actor.usr.barred';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ -- Fail if we couldn't find any matchpoint (requires a default)
+ IF matchpoint_id IS NULL THEN
+ result.fail_part := 'no_matchpoint';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
+
+ IF hold_test.holdable IS FALSE THEN
+ result.fail_part := 'config.hold_matrix_test.holdable';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+
+ IF hold_test.transit_range IS NOT NULL THEN
+ SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
+ IF hold_test.distance_is_from_owner THEN
+ SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number;
+ ELSE
+ SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
+ END IF;
+
+ PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
+
+ IF NOT FOUND THEN
+ result.fail_part := 'transit_range';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+ END IF;
+
+ IF NOT retargetting THEN
+ FOR standing_penalty IN
+ SELECT DISTINCT csp.*
+ FROM actor.usr_standing_penalty usp
+ JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+ WHERE usr = match_user
+ AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+ AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+ AND csp.block_list LIKE '%HOLD%' LOOP
+
+ result.fail_part := standing_penalty.name;
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END LOOP;
+
+ IF hold_test.stop_blocked_user IS TRUE THEN
+ FOR standing_penalty IN
+ SELECT DISTINCT csp.*
+ FROM actor.usr_standing_penalty usp
+ JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+ WHERE usr = match_user
+ AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+ AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+ AND csp.block_list LIKE '%CIRC%' LOOP
+
+ result.fail_part := standing_penalty.name;
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END LOOP;
+ END IF;
+
+ IF hold_test.max_holds IS NOT NULL THEN
+ SELECT INTO hold_count COUNT(*)
+ FROM action.hold_request
+ WHERE usr = match_user
+ AND fulfillment_time IS NULL
+ AND cancel_time IS NULL
+ AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
+
+ IF hold_count >= hold_test.max_holds THEN
+ result.fail_part := 'config.hold_matrix_test.max_holds';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+ END IF;
+ END IF;
+
+ IF item_object.age_protect IS NOT NULL THEN
+ SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
+
+ IF item_object.create_date + age_protect_object.age > NOW() THEN
+ IF hold_test.distance_is_from_owner THEN
+ SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
+ ELSE
+ SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
+ END IF;
+
+ IF hold_transit_prox > age_protect_object.prox THEN
+ result.fail_part := 'config.rule_age_hold_protect.prox';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+ END IF;
+ END IF;
+
+ IF NOT done THEN
+ RETURN NEXT result;
+ END IF;
+
+ RETURN;
+END;
+$func$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, FALSE );
+$func$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION action.hold_retarget_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS $func$
+ SELECT * FROM action.hold_request_permit_test( $1, $2, $3, $4, $5, TRUE );
+$func$ LANGUAGE SQL;
+
+COMMIT;
+
--- /dev/null
+INSERT INTO config.upgrade_log (version) VALUES ('0429'); -- miker, for shame
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0430'); -- miker
+
+ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN strict_ou_match BOOL NOT NULL DEFAULT FALSE;
+
+CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
+DECLARE
+ current_requestor_group permission.grp_tree%ROWTYPE;
+ requestor_object actor.usr%ROWTYPE;
+ user_object actor.usr%ROWTYPE;
+ item_object asset.copy%ROWTYPE;
+ item_cn_object asset.call_number%ROWTYPE;
+ rec_descriptor metabib.rec_descriptor%ROWTYPE;
+ current_mp_weight FLOAT;
+ matchpoint_weight FLOAT;
+ tmp_weight FLOAT;
+ current_mp config.hold_matrix_matchpoint%ROWTYPE;
+ matchpoint config.hold_matrix_matchpoint%ROWTYPE;
+BEGIN
+ SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+ SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
+ SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+ SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
+ SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r WHERE r.record = item_cn_object.record;
+
+ PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
+
+ IF NOT FOUND THEN
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = requestor_object.profile;
+ ELSE
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = user_object.profile;
+ END IF;
+
+ LOOP
+ -- for each potential matchpoint for this ou and group ...
+ FOR current_mp IN
+ SELECT m.*
+ FROM config.hold_matrix_matchpoint m
+ WHERE m.requestor_grp = current_requestor_group.id AND m.active
+ ORDER BY CASE WHEN m.circ_modifier IS NOT NULL THEN 16 ELSE 0 END +
+ CASE WHEN m.juvenile_flag IS NOT NULL THEN 16 ELSE 0 END +
+ CASE WHEN m.marc_type IS NOT NULL THEN 8 ELSE 0 END +
+ CASE WHEN m.marc_form IS NOT NULL THEN 4 ELSE 0 END +
+ CASE WHEN m.marc_vr_format IS NOT NULL THEN 2 ELSE 0 END +
+ CASE WHEN m.ref_flag IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
+
+ current_mp_weight := 5.0;
+
+ IF current_mp.circ_modifier IS NOT NULL THEN
+ CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier OR item_object.circ_modifier IS NULL;
+ END IF;
+
+ IF current_mp.marc_type IS NOT NULL THEN
+ IF item_object.circ_as_type IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
+ ELSE
+ CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
+ END IF;
+ END IF;
+
+ IF current_mp.marc_form IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
+ END IF;
+
+ IF current_mp.marc_vr_format IS NOT NULL THEN
+ CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
+ END IF;
+
+ IF current_mp.juvenile_flag IS NOT NULL THEN
+ CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
+ END IF;
+
+ IF current_mp.ref_flag IS NOT NULL THEN
+ CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
+ END IF;
+
+
+ -- caclulate the rule match weight
+ IF current_mp.item_owning_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.item_owning_ou = item_cn_object.owning_lib THEN 1.0 ELSE 0.0 END;
+ END IF;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.item_circ_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.item_circ_ou = item_object.circ_lib THEN 1.0 ELSE 0.0 END;
+ END IF;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.pickup_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.pickup_ou = pickiup_ou THEN 1.0 ELSE 0.0 END;
+ END IF;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.request_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.request_ou = request_ou THEN 1.0 ELSE 0.0 END;
+ END IF;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ IF current_mp.user_home_ou IS NOT NULL THEN
+ IF NOT current_mp.strict_ou_match THEN
+ SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
+ ELSE
+ tmp_weight := CASE WHEN current_mp.user_home_ou = user_object.home_ou THEN 1.0 ELSE 0.0 END;
+ END IF;
+ current_mp_weight := current_mp_weight - tmp_weight;
+ END IF;
+
+ -- set the matchpoint if we found the best one
+ IF matchpoint_weight IS NULL OR matchpoint_weight > current_mp_weight THEN
+ matchpoint = current_mp;
+ matchpoint_weight = current_mp_weight;
+ END IF;
+
+ END LOOP;
+
+ EXIT WHEN current_requestor_group.parent IS NULL OR matchpoint.id IS NOT NULL;
+
+ SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = current_requestor_group.parent;
+ END LOOP;
+
+ RETURN matchpoint.id;
+END;
+$func$ LANGUAGE plpgsql;
+
+COMMIT;
+
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0431'); -- miker
+
+CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
+DECLARE
+ matchpoint_id INT;
+ user_object actor.usr%ROWTYPE;
+ age_protect_object config.rule_age_hold_protect%ROWTYPE;
+ standing_penalty config.standing_penalty%ROWTYPE;
+ transit_range_ou_type actor.org_unit_type%ROWTYPE;
+ transit_source actor.org_unit%ROWTYPE;
+ item_object asset.copy%ROWTYPE;
+ ou_skip actor.org_unit_setting%ROWTYPE;
+ result action.matrix_test_result;
+ hold_test config.hold_matrix_matchpoint%ROWTYPE;
+ hold_count INT;
+ hold_transit_prox INT;
+ frozen_hold_count INT;
+ context_org_list INT[];
+ done BOOL := FALSE;
+BEGIN
+ SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
+ SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
+
+ result.success := TRUE;
+
+ -- Fail if we couldn't find a user
+ IF user_object.id IS NULL THEN
+ result.fail_part := 'no_user';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
+
+ -- Fail if we couldn't find a copy
+ IF item_object.id IS NULL THEN
+ result.fail_part := 'no_item';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
+ result.matchpoint := matchpoint_id;
+
+ SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
+
+ -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
+ IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
+ result.fail_part := 'circ.holds.target_skip_me';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ -- Fail if user is barred
+ IF user_object.barred IS TRUE THEN
+ result.fail_part := 'actor.usr.barred';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ -- Fail if we couldn't find any matchpoint (requires a default)
+ IF matchpoint_id IS NULL THEN
+ result.fail_part := 'no_matchpoint';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ RETURN;
+ END IF;
+
+ SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
+
+ IF hold_test.holdable IS FALSE THEN
+ result.fail_part := 'config.hold_matrix_test.holdable';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+
+ IF hold_test.transit_range IS NOT NULL THEN
+ SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
+ IF hold_test.distance_is_from_owner THEN
+ SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number;
+ ELSE
+ SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
+ END IF;
+
+ PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
+
+ IF NOT FOUND THEN
+ result.fail_part := 'transit_range';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+ END IF;
+
+ FOR standing_penalty IN
+ SELECT DISTINCT csp.*
+ FROM actor.usr_standing_penalty usp
+ JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+ WHERE usr = match_user
+ AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+ AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+ AND csp.block_list LIKE '%HOLD%' LOOP
+
+ result.fail_part := standing_penalty.name;
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END LOOP;
+
+ IF hold_test.stop_blocked_user IS TRUE THEN
+ FOR standing_penalty IN
+ SELECT DISTINCT csp.*
+ FROM actor.usr_standing_penalty usp
+ JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+ WHERE usr = match_user
+ AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
+ AND (usp.stop_date IS NULL or usp.stop_date > NOW())
+ AND csp.block_list LIKE '%CIRC%' LOOP
+
+ result.fail_part := standing_penalty.name;
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END LOOP;
+ END IF;
+
+ IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
+ SELECT INTO hold_count COUNT(*)
+ FROM action.hold_request
+ WHERE usr = match_user
+ AND fulfillment_time IS NULL
+ AND cancel_time IS NULL
+ AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
+
+ IF hold_count >= hold_test.max_holds THEN
+ result.fail_part := 'config.hold_matrix_test.max_holds';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+ END IF;
+
+ IF item_object.age_protect IS NOT NULL THEN
+ SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
+
+ IF item_object.create_date + age_protect_object.age > NOW() THEN
+ IF hold_test.distance_is_from_owner THEN
+ SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
+ ELSE
+ SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
+ END IF;
+
+ IF hold_transit_prox > age_protect_object.prox THEN
+ result.fail_part := 'config.rule_age_hold_protect.prox';
+ result.success := FALSE;
+ done := TRUE;
+ RETURN NEXT result;
+ END IF;
+ END IF;
+ END IF;
+
+ IF NOT done THEN
+ RETURN NEXT result;
+ END IF;
+
+ RETURN;
+END;
+$func$ LANGUAGE plpgsql;
+
+COMMIT;
+
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0432'); -- Scott McKellar
+
+ALTER TABLE config.rule_circ_duration
+ ADD COLUMN date_ceiling TIMESTAMPTZ;
+
+CREATE TABLE config.hard_due_date (
+ id SERIAL PRIMARY KEY,
+ duration_rule INT NOT NULL REFERENCES config.rule_circ_duration (id)
+ DEFERRABLE INITIALLY DEFERRED,
+ ceiling_date TIMESTAMPTZ NOT NULL,
+ active_date TIMESTAMPTZ NOT NULL
+);
+
+COMMIT;
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0433'); -- atz
+
+UPDATE action_trigger.event_definition SET delay='00:00:00', template=$$
+[%- USE date -%]
+[%# start JEDI document
+ # Vendor specific kludges:
+ # BT - vendcode goes to NAD/BY *suffix* w/ 91 qualifier
+ # INGRAM - vendcode goes to NAD/BY *segment* w/ 91 qualifier (separately)
+ # BRODART - vendcode goes to FTX segment (lineitem level)
+-%]
+[%-
+IF target.provider.edi_default.vendcode && target.provider.code == 'BRODART';
+ xtra_ftx = target.provider.edi_default.vendcode;
+END;
+-%]
+[%- BLOCK big_block -%]
+{
+ "recipient":"[% target.provider.san %]",
+ "sender":"[% target.ordering_agency.mailing_address.san %]",
+ "body": [{
+ "ORDERS":[ "order", {
+ "po_number":[% target.id %],
+ "date":"[% date.format(date.now, '%Y%m%d') %]",
+ "buyer":[
+ [% IF target.provider.edi_default.vendcode && (target.provider.code == 'BT' || target.provider.name.match('(?i)^BAKER & TAYLOR')) -%]
+ {"id-qualifier": 91, "id":"[% target.ordering_agency.mailing_address.san _ ' ' _ target.provider.edi_default.vendcode %]"}
+ [%- ELSIF target.provider.edi_default.vendcode && target.provider.code == 'INGRAM' -%]
+ {"id":"[% target.ordering_agency.mailing_address.san %]"},
+ {"id-qualifier": 91, "id":"[% target.provider.edi_default.vendcode %]"}
+ [%- ELSE -%]
+ {"id":"[% target.ordering_agency.mailing_address.san %]"}
+ [%- END -%]
+ ],
+ "vendor":[
+ [%- # target.provider.name (target.provider.id) -%]
+ "[% target.provider.san %]",
+ {"id-qualifier": 92, "id":"[% target.provider.id %]"}
+ ],
+ "currency":"[% target.provider.currency_type %]",
+
+ "items":[
+ [%- FOR li IN target.lineitems %]
+ {
+ "line_index":"[% li.id %]",
+ "identifiers":[ [%-# li.isbns = helpers.get_li_isbns(li.attributes) %]
+ [% FOR isbn IN helpers.get_li_isbns(li.attributes) -%]
+ [% IF isbn.length == 13 -%]
+ {"id-qualifier":"EN","id":"[% isbn %]"},
+ [% ELSE -%]
+ {"id-qualifier":"IB","id":"[% isbn %]"},
+ [%- END %]
+ [% END %]
+ {"id-qualifier":"IN","id":"[% li.id %]"}
+ ],
+ "price":[% li.estimated_unit_price || '0.00' %],
+ "desc":[
+ {"BTI":"[% helpers.get_li_attr('title', '', li.attributes) %]"},
+ {"BPU":"[% helpers.get_li_attr('publisher', '', li.attributes) %]"},
+ {"BPD":"[% helpers.get_li_attr('pubdate', '', li.attributes) %]"},
+ {"BPH":"[% helpers.get_li_attr('pagination','', li.attributes) %]"}
+ ],
+ [%- ftx_vals = [];
+ FOR note IN li.lineitem_notes;
+ NEXT UNLESS note.vendor_public == 't';
+ ftx_vals.push(note.value);
+ END;
+ IF xtra_ftx; ftx_vals.unshift(xtra_ftx); END;
+ IF ftx_vals.size == 0; ftx_vals.unshift(''); END; # BT needs FTX+LIN for every LI, even if it is an empty one
+ -%]
+
+ "free-text":[
+ [% FOR note IN ftx_vals -%] "[% note %]"[% UNLESS loop.last %], [% END %][% END %]
+ ],
+ "quantity":[% li.lineitem_details.size %]
+ }[% UNLESS loop.last %],[% END %]
+ [%-# TODO: lineitem details (later) -%]
+ [% END %]
+ ],
+ "line_items":[% target.lineitems.size %]
+ }] [%# close ORDERS array %]
+ }] [%# close body array %]
+}
+[% END %]
+[% tempo = PROCESS big_block; helpers.escape_json(tempo) %]
+
+$$
+WHERE id=23;
+
+COMMIT;
--- /dev/null
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0434');
+
+INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('template_merge','Template Merge Container');
+
+COMMIT;
+
--- /dev/null
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0435'); -- miker
+
+CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT ) RETURNS TEXT AS $_$
+
+ use MARC::Record;
+ use MARC::File::XML (BinaryEncoding => 'UTF-8');
+ use strict;
+
+ my $target_xml = shift;
+ my $source_xml = shift;
+ my $field_spec = shift;
+
+ my $target_r = MARC::Record->new_from_xml( $target_xml );
+ my $source_r = MARC::Record->new_from_xml( $source_xml );
+
+ return $target_xml unless ($target_r && $source_r);
+
+ my @field_list = split(',', $field_spec);
+
+ my %fields;
+ for my $f (@field_list) {
+ $f =~ s/^\s*//; $f =~ s/\s*$//;
+ if ($f =~ /^(.{3})(\w*)(?:\[([^]]*)\])?$/) {
+ my $field = $1;
+ $field =~ s/\s+//;
+ my $sf = $2;
+ $sf =~ s/\s+//;
+ my $match = $3;
+ $match =~ s/^\s*//; $match =~ s/\s*$//;
+ $fields{$field} = { sf => [ split('', $sf) ] };
+ if ($match) {
+ my ($msf,$mre) = split('~', $match);
+ if (length($msf) > 0 and length($mre) > 0) {
+ $msf =~ s/^\s*//; $msf =~ s/\s*$//;
+ $mre =~ s/^\s*//; $mre =~ s/\s*$//;
+ $fields{$field}{match} = { sf => $msf, re => qr/$mre/ };
+ }
+ }
+ }
+ }
+
+ for my $f ( keys %fields) {
+ if ( @{$fields{$f}{sf}} ) {
+ for my $from_field ($source_r->field( $f )) {
+ my @tos = $target_r->field( $f );
+ if (!@tos) {
+ my @new_fields = map { $_->clone } $source_r->field( $f );
+ $target_r->insert_fields_ordered( @new_fields );
+ } else {
+ for my $to_field (@tos) {
+ if (exists($fields{$f}{match})) {
+ next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
+ }
+ my @new_sf = map { ($_ => $from_field->subfield($_)) } @{$fields{$f}{sf}};
+ $to_field->add_subfields( @new_sf );
+ }
+ }
+ }
+ } else {
+ my @new_fields = map { $_->clone } $source_r->field( $f );
+ $target_r->insert_fields_ordered( @new_fields );
+ }
+ }
+
+ $target_xml = $target_r->as_xml_record;
+ $target_xml =~ s/^<\?.+?\?>$//mo;
+ $target_xml =~ s/\n//sgo;
+ $target_xml =~ s/>\s+</></sgo;
+
+ return $target_xml;
+
+$_$ LANGUAGE PLPERLU;
+
+COMMIT;
+
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0436'); -- miker
+
+CREATE OR REPLACE FUNCTION vandelay.compile_profile ( incoming_xml TEXT ) RETURNS vandelay.compile_profile AS $_$
+DECLARE
+ output vandelay.compile_profile%ROWTYPE;
+ profile vandelay.merge_profile%ROWTYPE;
+ profile_tmpl TEXT;
+ profile_tmpl_owner TEXT;
+ add_rule TEXT := '';
+ strip_rule TEXT := '';
+ replace_rule TEXT := '';
+ preserve_rule TEXT := '';
+
+BEGIN
+
+ profile_tmpl := (oils_xpath('//*[@tag="905"]/*[@code="t"]/text()',incoming_xml))[1];
+ profile_tmpl_owner := (oils_xpath('//*[@tag="905"]/*[@code="o"]/text()',incoming_xml))[1];
+
+ IF profile_tmpl IS NOT NULL AND profile_tmpl <> '' AND profile_tmpl_owner IS NOT NULL AND profile_tmpl_owner <> '' THEN
+ SELECT p.* INTO profile
+ FROM vandelay.merge_profile p
+ JOIN actor.org_unit u ON (u.id = p.owner)
+ WHERE p.name = profile_tmpl
+ AND u.shortname = profile_tmpl_owner;
+
+ IF profile.id IS NOT NULL THEN
+ add_rule := COALESCE(profile.add_spec,'');
+ strip_rule := COALESCE(profile.strip_spec,'');
+ replace_rule := COALESCE(profile.replace_spec,'');
+ preserve_rule := COALESCE(profile.preserve_spec,'');
+ END IF;
+ END IF;
+
+ add_rule := add_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="a"]/text()',incoming_xml),','),'');
+ strip_rule := strip_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="d"]/text()',incoming_xml),','),'');
+ replace_rule := replace_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="r"]/text()',incoming_xml),','),'');
+ preserve_rule := preserve_rule || ',' || COALESCE(ARRAY_TO_STRING(oils_xpath('//*[@tag="905"]/*[@code="p"]/text()',incoming_xml),','),'');
+
+ output.add_rule := BTRIM(add_rule,',');
+ output.replace_rule := BTRIM(replace_rule,',');
+ output.strip_rule := BTRIM(strip_rule,',');
+ output.preserve_rule := BTRIM(preserve_rule,',');
+
+ RETURN output;
+END;
+$_$ LANGUAGE PLPGSQL;
+
+COMMIT;
--- /dev/null
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0437'); -- miker
+
+DROP INDEX asset.asset_call_number_label_sortkey;
+CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(cast(label_sortkey as bytea));
+
+COMMIT;
--- /dev/null
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0438'); -- miker
+
+DROP INDEX asset.asset_call_number_upper_label_id_owning_lib_idx;
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (cast(upper(label) as bytea),id,owning_lib);
+
+COMMIT;
--- /dev/null
+
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0439'); -- miker
+
+CREATE OR REPLACE FUNCTION oils_text_as_bytea (TEXT) RETURNS BYTEA AS $_$
+ SELECT CAST(REGEXP_REPLACE($1, $$\\$$, $$\\\\$$, 'g') AS BYTEA);
+$_$ LANGUAGE SQL IMMUTABLE;
+
+
+DROP INDEX asset.asset_call_number_upper_label_id_owning_lib_idx;
+CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(label),id,owning_lib);
+
+DROP INDEX asset.asset_call_number_label_sortkey;
+CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(oils_text_as_bytea(label_sortkey));
+
+COMMIT;
my $opt_hooks;
my $opt_process_hooks = 0;
my $opt_granularity = undef;
+my $opt_gran_only = undef;
(-f $opt_custom_filter) or undef($opt_custom_filter); # discard default if no file exists
'run-pending' => \$opt_run_pending,
'hooks=s' => \$opt_hooks,
'granularity=s' => \$opt_granularity,
+ 'granularity-only' => \$opt_gran_only,
'process-hooks' => \$opt_process_hooks,
'debug-stdout' => \$opt_debug_stdout,
'custom-filters=s' => \$opt_custom_filter,
my $max_sleep = $opt_max_sleep;
+#XXX need to figure out why this is required...
+$opt_gran_only = $opt_granularity ? 1 : 0;
+
+$opt_lockfile .= '.' . $opt_granularity if ($opt_granularity && $opt_gran_only);
+
# typical passive hook filters
my $hook_handlers = {
--granularity=<label>
Run events with {label} granularity setting, or no granularity setting
+ --granularity-only
+ Used in combination with --granularity, prevents the running of events with no granularity setting
+
--debug-stdout
Print server responses to stdout (as JSON) for debugging
sub run_pending {
$opt_verbose and print "run_pending: " .
- ($opt_run_pending ? ($opt_granularity || 'ALL granularity') : 'SKIPPING') . "\n";
+ ($opt_run_pending ?
+ ($opt_granularity ?
+ ( $opt_granularity . (
+ $opt_gran_only ?
+ ' ONLY' :
+ ' and NON-GRANULAR'
+ )
+ ) :
+ 'NON-GRANULAR'
+ ) :
+ 'SKIPPING'
+ ) . "\n";
+
return unless $opt_run_pending;
my $ses = OpenSRF::AppSession->create('open-ils.trigger');
- my $req = $ses->request('open-ils.trigger.event.run_all_pending' => $opt_granularity);
+ my $req = $ses->request('open-ils.trigger.event.run_all_pending' => $opt_granularity => $opt_gran_only);
my $check_lockfile = 1;
while (my $resp = $req->recv(timeout => $req_timeout)) {
# check the lockfile
if (-e $opt_lockfile) {
- die "I'm already running with lockfile $opt_lockfile\n" if (!$opt_process_hooks);
+ die "I'm already running with lockfile $opt_lockfile\n" unless ($opt_process_hooks or $opt_granularity);
# sleeping loop if we're in --process-hooks mode
while ($max_sleep >= 0 && sleep(1)) {
last unless ( -e $opt_lockfile );
__END__
-=pod
-
=head1 NAME
edi_fetcher.pl - A script for retrieving and processing EDI files from remote accounts.
=head1 OPTIONS
- --account=[id] Target one account, whether or not it is inactive.
- --inactive Includes inactive provider accounts (default OFF, forced ON if --account specified)
+ --account=[id] Target one account, whether or not it is inactive.
+ --inactive Includes inactive provider accounts (default OFF, forced ON if --account specified)
=head1 ARGUMENTS
my %defaults = (
'quiet' => 0,
+ 'test' => 0, # TODO
'max-batch-size=i' => -1
);
$opts->{verbose} = 0 if $opts->{quiet};
+print "FTP_PASSIVE is ", ($ENV{FTP_PASSIVE} ? "ON" : "OFF"), "\n";
-# print Dumper($defs);
print "\nHook '$hook' is used in ", scalar(@$defs), " event definition(s):\n";
$Data::Dumper::Indent = 1;
if ($opts->{verbose}) {
# $subq->{'select'}->{'acqedim'} = ['id', 'purchase_order', 'message_type', 'status'];
my $excluded = $e->json_query($subq);
- print "Excluded: ", scalar(@$excluded), " purchase order(s):\n", Dumper(\@$excluded), "\n";
+ print "Excluded: ", scalar(@$excluded), " purchase order(s):\n";
+ my $z = 0;
+ print map {sprintf "%7d%s", $_, (++$z % 5) ? '' : "\n"} sort {$a <=> $b} map {$_->{purchase_order}} @$excluded;
+ print "\n";
}
my $events = $e->json_query($query);
print "\ntarget->provider->edi_default->id: ", $target->provider->edi_default->id, "\n";
my $logstr2 = sprintf "event %s, PO %s, template_output %s", $_->{id}, $message->purchase_order, $event->template_output->id;
+ if ($opts->{test}) {
+ print "Test mode, skipping translation/send\n";
+ next;
+ }
+
printf "\nNow calling attempt_translation for $logstr2\n\n";
unless (OpenILS::Application::Acq::EDI->attempt_translation($message, 1)) {
}
print "\ndone\n";
+
+__END__
+
+=head1 NAME
+
+edi_pusher.pl - A script for generating and sending EDI files to remote accounts.
+
+=head1 DESCRIPTION
+
+This script is expected to be run via crontab, for the purpose of retrieving vendor EDI files.
+
+=head1 OPTIONS
+
+ --max-batch-size=i Limit the processing to a set number of events.
+
+=head1 TODO
+
+More docs here.
+
+=head1 USAGE
+
+B<FTP_PASSIVE=1> is recommended. Depending on your vendors' and your own network environments, you may want to set/export
+the environmental variable FTP_PASSIVE like:
+
+ export FTP_PASSIVE=1
+ # or
+ FTP_PASSIVE=1 Open-ILS/src/support-scripts/edi_pusher.pl
+
+=head1 SEE ALSO
+
+ OpenILS::Utils::Cronscript
+ edi_fetcher.pl
+
+=head1 AUTHOR
+
+Joe Atzberger <jatzberger@esilibrary.com>
+
+=cut
use OpenSRF::Utils::SettingsClient;
use OpenILS::Application::AppUtils;
use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::CStoreEditor;
use MARC::Record;
use MARC::File::XML;
my @formats = qw/USMARC UNIMARC XML BRE/;
-my ($config,$format,$encoding,$location,$dollarsign,$idl,$help,$holdings,$timeout) = ('/openils/conf/opensrf_core.xml','USMARC','MARC8','','$',0,undef,undef,0);
+my ($config,$format,$encoding,$location,$dollarsign,$idl,$help,$holdings,$timeout,$export_mfhd) = ('/openils/conf/opensrf_core.xml','USMARC','MARC8','','$',0,undef,undef,0,undef);
GetOptions(
'help' => \$help,
'items' => \$holdings,
+ 'mfhd' => \$export_mfhd,
'location=s' => \$location,
'money=s' => \$dollarsign,
'config=s' => \$config,
Usage: $0 [options]
--help or -h This screen.
--config or -c Configuration file [/openils/conf/opensrf_core.xml]
- --format or -f Output format (USMARC, UNIMARC, XML) [USMARC]
+ --format or -f Output format (USMARC, UNIMARC, XML, BRE) [USMARC]
--encoding or -e Output Encoding (UTF-8, ISO-8859-?, MARC8) [MARC8]
--items or -i Include items (holdings) in the output
+ --mfhd Export serial MFHD records for associated bib records
+ Not compatible with --format=BRE
--xml-idl or -x Location of the IDL XML
--location or -l MARC Location Code for holdings from
http://www.loc.gov/marc/organizations/orgshome.html
Fieldmapper->import(IDL => $idl);
my $ses = OpenSRF::AppSession->create('open-ils.cstore');
+OpenILS::Utils::CStoreEditor::init();
+my $editor = OpenILS::Utils::CStoreEditor->new();
print <<HEADER if ($format eq 'XML');
<?xml version="1.0" encoding="$encoding"?>
import MARC::File::XML; # reset SAX parser so that one bad record doesn't kill the entire export
};
+ if ($export_mfhd) {
+ my $mfhds = $editor->search_serial_record_entry({record => $i, deleted => 'f'});
+ foreach my $mfhd (@$mfhds) {
+ try {
+ my $r = MARC::Record->new_from_xml( $mfhd->marc, $encoding, $format );
+
+ if (uc($format) eq 'XML') {
+ my $xml = $r->as_xml_record;
+ $xml =~ s/^<\?.+?\?>$//mo;
+ print $xml;
+ } elsif (uc($format) eq 'UNIMARC') {
+ print $r->as_usmarc;
+ } elsif (uc($format) eq 'USMARC') {
+ print $r->as_usmarc;
+ }
+ } otherwise {
+ my $e = shift;
+ warn "\n$e\n";
+ import MARC::File::XML; # reset SAX parser so that one bad record doesn't kill the entire export
+ };
+ }
+ }
+
stats() if (! ($count{bib} % 50 ));
}
--- /dev/null
+BUTTON_SUBMIT=Envoyer
+REQUEST_TITLE=Formulaire de demande de réinitialisation du mot de passe du réseau de la Bibliothèque
+IDENTIFY_YOURSELF=Entrez votre nom d’utilisateur ou votre code à barres pour indiquer votre compte de bibliothèque et demander la réinitialisation de votre mot de passe.
+REQUEST_SUCCESS=Votre nom d’utilisateur ou votre code à barres a été présenté pour la réinitialisation de votre mot de passe. S’il existe un compte correspondant assorti d’une adresse électronique, vous recevrez bientôt à cette adresse un message contenant les instructions pour réinitialiser votre mot de passe.
+BARCODE_PROMPT=Code à barres :
+USERNAME_PROMPT=Nom d’utilisateur :
+EMAIL_PROMPT=Adresse électronique liée au compte :
+NO_SESSION=Impossible de trouver la session de réinitialisation du mot de passe demandée.
+NO_MATCH=Les mots de passe ne concordent pas. Veuillez réessayer.
+NOT_ACTIVE=Cette demande de réinitialisation du mot de passe est inactive. Votre mot de passe n’a pas été réinitialisé.
+NOT_STRONG=Le mot de passe que vous avez choisi n’est pas assez complexe pour protéger votre compte. Votre mot de passe n’a pas été réinitialisé.
+SUCCESS=Mot de passe réinitialisé.
+TITLE=Réinitialiser le mot de passe du réseau de la Bibliothèque
+PASSWORD_PROMPT=Nouveau mot de passe :
+PASSWORD_PROMPT2=Entrez de nouveau le mot de passe :
}
editor_pane_application_perm.setValue( this.store.getValue( current_group, 'application_perm' ) );
+ editor_pane_hold_priority.setValue( this.store.getValue( current_group, 'hold_priority' ) );
editor_pane_usergroup.setChecked( this.store.getValue( current_group, 'usergroup' ) == 't' ? true : false );
]]>
</td>
</tr>
<tr>
+ <th>&conify.grp_tree.hold_priority.label;</th>
+ <td>
+ <div
+ id="editor_pane_hold_priority"
+ dojoType="dijit.form.NumberSpinner"
+ jsId="editor_pane_hold_priority"
+ >
+ <script type="dojo/connect" event="onChange">
+<![CDATA[
+ if (current_group && this.getValue()) {
+ group_store.setValue( current_group, "hold_priority", this.getValue() );
+ }
+]]>
+ </script>
+ </div>
+ </td>
+ </tr>
+ <tr>
<th>&conify.grp_tree.parent_group.label;</th>
<td>
<div
subfield : function (code) {
var list = dojo.filter( this.subfields, function (s) {
- if (s[0] == code) return true; return true;
+ if (s[0] == code) return true; return false;
});
if (list.length == 1) return list[0];
return list;
constructor : function(kwargs) {
this.fields = [];
- this.leader = '';
+ this.leader = '00000cam a2200205Ka 4500';
if (kwargs.delimiter) this.delimiter = kwargs.delimiter;
if (kwargs.onLoad) this.onLoad = kwargs.onLoad;
return list;
},
- subfield : function (spec, code) { return this.field(spec)[0].subfield(code) },
+ subfield : function (spec, code) {
+ var f = this.field(spec);
+ if (dojo.isArray(f)) f = f[0];
+ return f.subfield(code)
+ },
appendFields : function () {
var me = this;
fromXmlDocument : function (mxml) {
var me = this;
- me.leader = dojox.xml.parser.textContent(dojo.query('leader', mxml)[0]) || '';
+ me.leader = dojox.xml.parser.textContent(dojo.query('leader', mxml)[0]) || '00000cam a2200205Ka 4500';
dojo.forEach( dojo.query('controlfield', mxml), function (cf) {
me.fields.push(
// skip comment lines
} else if (isControlField(current_line)) {
if (line_tag(current_line) == 'LDR') {
- me.leader = cf_line_data(current_line) || '';
+ me.leader = cf_line_data(current_line) || '00000cam a2200205Ka 4500';
} else {
me.fields.push(
new MARC.Field({
this.locale = kwargs.locale || OpenSRF.locale || 'en-US';
this.nodelay = kwargs.delay == false;
+ if (this.xml && this.xml instanceof String)
+ this.xml = dojox.xml.parser.parse(this.xml);
+
this.mode = 'biblio-record_entry';
this.default_datatype = 'marcxml-uris';
if (kwargs.metarecord) {
},
textContent : function (node) {
- var content = '';
if (node) {
- if(window.ActiveXObject) content = node.text;
- else content = node.textContent;
+ if (node instanceof HTMLElement) return node.innerText || node.textContent;
+ return dojox.xml.parser.textContent(node);
}
- return content;
+ return '';
},
render : function() {
- var all_slots = dojo.query('*[type^=opac/slot-data]', this.root);
+ var all_slots = dojo.query('*[type^="opac/slot-data"]', this.root);
var default_datatype = this.default_datatype;
var slots = {};
var item_limit = parseInt(slot.getAttribute('limit'));
var item_offset = parseInt(slot.getAttribute('offset')) || 0;
- var pre_render_callbacks = dojo.query( '*[type=opac/call-back+pre-render]', slot );
- var post_render_callbacks = dojo.query( '*[type=opac/call-back+post-render]', slot );
- var pre_query_callbacks = dojo.query( '*[type=opac/call-back+pre-query]', slot );
- var post_query_callbacks = dojo.query( '*[type=opac/call-back+post-query]', slot );
+ var pre_render_callbacks = dojo.query( '*[type="opac/call-back+pre-render"]', slot );
+ var post_render_callbacks = dojo.query( '*[type="opac/call-back+post-render"]', slot );
+ var pre_query_callbacks = dojo.query( '*[type="opac/call-back+pre-query"]', slot );
+ var post_query_callbacks = dojo.query( '*[type="opac/call-back+post-query"]', slot );
// Do pre-query stuff
dojo.forEach(pre_query_callbacks, function (cb) {
if (item_list.length) item_list = BT.subsetNL(item_list, item_offset, item_offset + item_limit);
}
- // Do post-query stuff, only if there's an item list!
+ // Do post-query stuff
dojo.forEach(post_query_callbacks, function (cb) {
try { (new Function( 'item_list', 'BT', 'slotXML', 'slot', unescape(cb.innerHTML) ))(item_list,BT,bib,slot) } catch (e) {/*meh*/}
});
var template_value_count = 0;
dojo.query(
- '*[type=opac/template-value]',
+ '*[type="opac/template-value"]',
slot
).orphan().forEach(function(x) {
var name = x.getAttribute('name');
if (template_value_count > 0) slot.innerHTML = dojo.string.substitute( unescape(slot.innerHTML), template_values );
}
- var handler_node = dojo.query( '*[type=opac/slot-format]', slot )[0];
+ var handler_node = dojo.query( '*[type="opac/slot-format"]', slot )[0];
if (handler_node) slot_handler = new Function('item_list', 'BT', 'slotXML', 'slot', 'item', dojox.xml.parser.textContent(handler_node) || handler_node.innerHTML);
else slot_handler = new Function('item_list', 'BT', 'slotXML', 'slot', 'item','return dojox.xml.parser.textContent(item) || item.innerHTML;');
session : null,
authtoken : null,
connnected : false,
+ authoritative : false,
constructor : function ( kwargs ) {
kwargs = kwargs || {};
this.authtoken = kwargs.authtoken;
+ this.authoritative = kwargs.authoritative;
this.session =
kwargs.session ||
return false;
}
},
-
+
+ _session_request : function ( args /* hash */, commitOnComplete /* set to true, else no */ ) {
+
+ var me = this;
+ var endstyle = 'rollback';
+ var aopts = dojo.mixin({}, args);
+ args = aopts;
+ if (commitOnComplete) endstyle = 'commit';
+
+ if (me.authoritative) {
+ if (!me.connected) me.connect();
+ if (args.timeout && !args.oncomplete && !args.onresponse) { // pure sync call
+ args.oncomplete = function (r) {
+ me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
+ me.session.disconnect();
+ me.disconnect();
+ };
+ } else if (args.oncomplete) { // there's an oncomplete, fire that, and then end the transaction
+ var orig_oncomplete = args.oncomplete;
+ args.oncomplete = function (r) {
+ var ret;
+ try {
+ ret = orig_oncomplete(r);
+ } finally {
+ me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
+ me.session.disconnect();
+ me.disconnect();
+ }
+ return ret;
+ };
+ }
+ me.session.request('open-ils.pcrud.transaction.begin', me.auth());
+ }
+ return me.session.request( args );
+ },
retrieve : function ( fm_class /* Fieldmapper class hint */, id /* Fieldmapper object primary key value */, opts /* Option hash */) {
if(!opts) opts = {};
if (!opts.async && !opts.timeout) req_hash.timeout = 10;
var _pcrud = this;
- var req = this.session.request( req_hash );
+ var req = this._session_request( req_hash );
if (!req.onerror)
req.onerror = function (r) { throw js2JSON(r); };
if (!opts.async && !opts.timeout) req_hash.timeout = 10;
var _pcrud = this;
- var req = this.session.request( req_hash );
+ var req = this._session_request( req_hash );
if (!req.onerror)
req.onerror = function (r) { throw js2JSON(r); };
if (!opts.async && !opts.timeout) req_hash.timeout = 10;
var _pcrud = this;
- var req = this.session.request( req_hash );
+ var req = this._session_request( req_hash );
if (!req.onerror)
req.onerror = function (r) { throw js2JSON(r); };
if(openils.XUL.isXUL()) {
try {
if(openils.XUL.enableXPConnect()) {
- var CacheClass = new Components.Constructor("@mozilla.org/openils_data_cache;1", "nsIOpenILS");
- return new CacheClass().wrappedJSObject.OpenILS.prototype.data;
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ var CacheClass = new Components.Constructor("@mozilla.org/openils_data_cache;1", "nsIOpenILS");
+ return new CacheClass().wrappedJSObject.OpenILS.prototype.data;
}
} catch(e) {
console.log("Error loading XUL stash: " + e);
+ return { 'error' : e };
}
}
- return {};
+ return { 'error' : 'openils.XUL.isXUL() == false' };
}
openils.XUL.newTab = function(path, tabInfo, options) {
{
+ "DEFAULT_ADDRESS_TYPE" : "MAILING",
"DELETE_ADDRESS" : "Delete address ${0}?",
"NEED_ADDRESS" : "An address is required during registration.",
"DUPE_PATRON_NAME" : "Found ${0} patron(s) with the same name",
* ---------------------------------------------------------------------------
*/
+/* Example markup:
+
+<div id='facetSidebarContainer' class='hide_me'>
+
+ <div class="side_bar_item" style="margin-top: 10px; font-weight: bold;">
+ <span>&navigate.facetRefine;</span>
+ </div>
+
+ <div
+ dojoType='openils.widget.FacetSidebar'
+ searchBox='facet_box'
+ searchSubmit='search_submit'
+ facetLimit='5'
+ maxValuesPerFacet='10'
+ classOrder='[{"name":"author","facetOrder":["personal","corporate"]},{"name":"subject","facetOrder":["topic"]},"series",{"name":"subject","facetOrder":["name","geographic"]}]'
+ >
+ <script type='dojo/method' event='populate'><![CDATA[
+ var f_sidebar = this;
+ attachEvt("result", "allRecordsReceived", function () {
+ if(!resultFacetKey) return;
+ if (f_sidebar.facetCacheKey) return; // already rendered it
+
+ dojo.removeClass('facetSidebarContainer','hide_me');
+
+ f_sidebar.facetCacheKey = resultFacetKey;
+ f_sidebar.render();
+ });
+ ]]></script>
+ </div>
+</div>
+
+ */
+
+
if(!dojo._hasResource["openils.widget.FacetSidebar"]) {
dojo._hasResource["openils.widget.FacetSidebar"] = true;
facetData : {},
facetCacheKey : '',
searchBox : '',
- classOrder : null,
+ classOrder : null, // Array of cmc.name values, OR array of objects with name and facetOrder properties
+ displayItemLimit : 999, // Number of distinctly described entries (classes or facets), that have values, to display from classOrder
searchSubmit : '',
facetLimit : 10,
maxValuesPerFacet : 100,
classes = [];
dojo.forEach(
this.classOrder,
- function(x) { classes.push({name:x}); }
+ function(x) {
+ if (dojo.isObject(x)) classes.push(x);
+ else classes.push({name:x});
+ }
);
}
+ var displayedItems = 0;
var me = this;
dojo.forEach(
classes,
function (x) {
- var possible_facets = dojo.filter(
- openils.widget.Searcher._cache.arr.cmf,
- function (y) {
- if (y.field_class == x.name && facetData[y.id]) return 1;
- return 0;
- }
- );
+ var possible_facets = [];
+ if (x.facetOrder) {
+ dojo.forEach(x.facetOrder, function(fname) {
+ var maybe_facet = dojo.filter(
+ openils.widget.Searcher._cache.arr.cmf,
+ function (y) {
+ if (y.field_class == x.name && y.name == fname && facetData[y.id]) {
+ if (displayedItems < me.displayItemLimit) {
+ displayedItems++;
+ return 1;
+ }
+ }
+ return 0;
+ }
+ )[0];
+ if (maybe_facet) possible_facets.push(maybe_facet);
+ });
+ } else {
+ possible_facets = dojo.filter(
+ openils.widget.Searcher._cache.arr.cmf,
+ function (y) {
+ if (y.field_class == x.name && facetData[y.id]) {
+ if (displayedItems < me.displayItemLimit) {
+ displayedItems++;
+ return 1;
+ }
+ }
+ return 0;
+ }
+ );
+ }
if (possible_facets.length > 0) me.addClass( x.name, possible_facets );
}
);
}
this.inherited(arguments);
- },
+ }
}
);
}
openils.User.authtoken = null;
openils.User.workstation = null;
+ if(!authtoken && openils.XUL.isXUL()) {
+ stash = openils.XUL.getStash();
+ authtoken = stash.session.key
+ }
+
if(authtoken) {
user = new openils.User();
delete user.sessionCache[authtoken];
dojo.addOnLoad(function(){
if(openils.XUL.isXUL()) {
// let XUL handle the login dialog
+ dump('getNewSession in base.js\n');
openils.XUL.getNewSession( function() { location.href = location.href } );
} else {
// in web-only mode, use the dojo login dialog
/* fetch any values set for this user */
userSettings = fieldmapper.standardRequest(
- ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve'],
+ ['open-ils.actor', 'open-ils.actor.patron.settings.retrieve.authoritative'],
{params : [openils.User.authtoken, userId, names]});
}
function uEditLoadUser(userId) {
var patron = fieldmapper.standardRequest(
- ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve'],
+ ['open-ils.actor', 'open-ils.actor.user.fleshed.retrieve.authoritative'],
{params : [openils.User.authtoken, userId]}
);
openils.Event.parse_and_raise(patron);
if(row.getAttribute('fmclass')) {
var widget = fleshFMRow(row, 'aua', {addr:id});
+ // make new addresses a default address type
+ if(id < 0 && row.getAttribute('fmfield') == 'address_type')
+ widget.widget.attr('value', localeStrings.DEFAULT_ADDRESS_TYPE);
+
// make new addresses valid by default
if(id < 0 && row.getAttribute('fmfield') == 'valid')
widget.widget.attr('value', true);
dijit.byId('authTerm').attr('value', term);
displayRecords();
}
- dojo.connect(dijit.byId('authTerm'), 'onBlur', function() {
- dijit.byId('authPage').attr('value', 0);
- displayRecords();
+
+ dojo.connect(dijit.byId('authAxis'), 'onKeyPress', function(evt) {
+ if (evt.keyCode == dojo.keys.ENTER) {
+ dijit.byId('authPage').attr('value', 0);
+ displayRecords();
+ }
+ });
+
+ dojo.connect(dijit.byId('authPage'), 'onKeyPress', function(evt) {
+ if (evt.keyCode == dojo.keys.ENTER) {
+ dijit.byId('authPage').attr('value', 0);
+ displayRecords();
+ }
});
+
dojo.connect(dijit.byId('authTerm'), 'onKeyPress', function(evt) {
if (evt.keyCode == dojo.keys.ENTER) {
dijit.byId('authPage').attr('value', 0);
}
});
+ dijit.byId('authTerm').focus();
+
}
dojo.addOnLoad(authListInit);
+ '/1' // replace with preceding line if OUs gain some meaning
+ '/' + dijit.byId('authTerm').attr('value')
+ '/' + dijit.byId('authPage').attr('value')
+ + '/' + '20' // 20 results per page
;
dojo.xhrGet({"url":url, "handleAs":"xml", "content":{"format":"marcxml"}, "preventCache": true, "load":displayAuthorities });
}
* Logout the patron and return to the login page
*/
SelfCheckManager.prototype.logoutPatron = function(print) {
+ progressDialog.show(true); // prevent patron from clicking logout link twice
if(print && this.checkouts.length) {
this.printSessionReceipt(
function() {
function loadEventDef() {
eventDefGranularity.attr('value', null);
edGrid.overrideEditWidgets.granularity = eventDefGranularity;
- edGrid.loadAll({order_by:{atevdef : 'hook'}});
+ edGrid.overrideEditWidgets.granularity.shove = {"create": ""};
+ edGrid.loadAll({order_by:{atevdef : 'name'}});
openils.widget.Textarea.width = '600px';
openils.widget.Textarea.height = '600px';
edGrid.overrideEditWidgetClass.template = 'openils.widget.Textarea';
function filterGrid(org) {
// fetch the locations and order entries
- var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
- orders = pcrud.search('acplo', {org : org}, {order_by : {acplo : 'position'}});
- locations = pcrud.search('acpl',
- {owning_lib : fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true)},
- {order_by : {acpl : 'name'}}
- );
+ if(!orders) {
+ var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
+ orders = pcrud.search('acplo', {org : org}, {order_by : {acplo : 'position'}});
+ locations = pcrud.search('acpl',
+ {owning_lib : fieldmapper.aou.orgNodeTrail(fieldmapper.aou.findOrgUnit(org), true)},
+ {order_by : {acpl : 'name'}}
+ );
+ }
// init the DnD environment
source.selectAll();
}
function applyChanges() {
- progressDialog.show(true);
- if(orders.length)
- deleteOrders(createOrders);
- else
- createOrders();
-}
-
-function deleteOrders(onload) {
- // delete the existing order entries in preparation for new ones
- var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
- pcrud.eliminate(
- orders,
- {
- async : true,
- oncomplete : function() {
- if(onload) onload();
- }
- }
- );
-}
-
-function createOrders() {
+ progressDialog.show();
var newOrders = [];
+ var contextOrg = contextOrgSelector.attr('value');
// pull the locations out of the DnD environment and create order entries for them
dojo.forEach(
var o = new fieldmapper.acplo();
o.position(newOrders.length + 1);
o.location(item.type[0]); // location.id() is stored in DnD item type
- o.org(contextOrgSelector.attr('value'));
+ o.org(contextOrg);
newOrders.push(o);
}
);
- // send the order entries off to the server
- var pcrud = new openils.PermaCrud({authtoken : user.authtoken});
- pcrud.create(
- newOrders,
+ fieldmapper.standardRequest(
+ ['open-ils.circ', 'open-ils.circ.copy_location_order.update'],
{
async : true,
- oncomplete : function(r) {
- progressDialog.hide();
- filterGrid(contextOrgSelector.attr('value'));
- }
+ params : [openils.User.authtoken, newOrders],
+ onresponse : function(r) {
+ if(r = openils.Util.readResponse(r)) {
+ if(r.orders) {
+ orders = r.order;
+ progressDialog.hide();
+ filterGrid(contextOrg);
+ return;
+ }
+ progressDialog.update(r);
+ }
+ },
}
);
}
};
new openils.User().buildPermOrgSelector(
- '"ADMIN_MERGE_PROFILE', profileContextOrgSelector, null, connect);
+ 'ADMIN_MERGE_PROFILE', profileContextOrgSelector, null, connect);
}
function buildProfileGrid() {
if ses != G.user.session, we also force a grab */
function grabUser(ses, force) {
+ _debug("grabUser auth token = " + ses);
if(!ses && isXUL()) {
stash = fetchXULStash();
ses = stash.session.key
if(!ses) {
ses = cookieManager.read(COOKIE_SES);
/* https cookies don't show up in http servers.. */
+ _debug("cookie auth token = " + ses);
}
if(!ses) return false;
if(isXUL()) {
dojo.require('openils.XUL');
+ dump('getNewSession in opac_utils.js\n');
openils.XUL.getNewSession(
function(success, authtoken) {
if(success) {
--- /dev/null
+<html>
+ <head>
+ <title>Printable Pull List</title>
+ <style type="text/css">
+ @import url('/js/dojo/dojo/resources/dojo.css');
+ @import url('/js/dojo/dijit/themes/tundra/tundra.css');
+ @import url('/js/dojo/dojox/widget/Toaster/Toaster.css');
+ @import url('/opac/skin/default/css/layout.css');
+ </style>
+ <style type="text/css">
+ #clear_holds_deck { margin-bottom: 1em; }
+ a { color: blue; text-decoration: underline; }
+ small { font-size: 9pt; }
+ body { font-size: 14pt; }
+ td {
+ padding-right: 1em;
+ padding-bottom: 1em;
+ border-bottom: 1px #999 dashed;
+ }
+ th {
+ text-align: left; font-weight: bold;
+ border-bottom: 1px #000 solid;
+ border-right: 1px #000 solid;
+ padding: 0.5em;
+ }
+ </style>
+ <!-- The OpenSRF API writ JS -->
+ <script language='javascript' src='/opac/common/js/utils.js' type='text/javascript'></script>
+ <script language='javascript' src='/opac/common/js/Cookies.js' type='text/javascript'></script>
+ <script language='javascript' src='/opac/common/js/CGI.js' type='text/javascript'></script>
+ <script language='javascript' src='/opac/common/js/JSON_v1.js' type='text/javascript'></script>
+ <!-- Dojo goodness -->
+ <script type="text/javascript">
+ var djConfig = {parseOnLoad:true,isDebug:false,AutoIDL:['aou','aout','pgt','ahr','acp','acn']};
+ var sort_order = ["acplo.position", "call_number", "request_time"];
+ </script>
+ <script type="text/javascript" src="/js/dojo/dojo/dojo.js"></script>
+ <script type="text/javascript" src="/js/dojo/dojo/openils_dojo.js"></script>
+ <script type="text/javascript" src="/js/dojo/dijit/dijit.js"></script>
+ <script type="text/javascript" src="/js/dojo/openils/AutoIDL.js"></script>
+ <script type="text/javascript" src="/js/dojo/openils/User.js"></script>
+ <script type="text/javascript" src="/js/dojo/openils/Util.js"></script>
+ <script type="text/javascript" src="/opac/extras/circ/alt_holds_print.js"></script>
+ <script type="text/javascript">
+ function my_init() {
+ cgi = new CGI();
+ authtoken = (typeof ses == "function" ? ses() : 0) ||
+ cgi.param("ses") || dojo.cookie("ses");
+
+ if (cgi.param("do") == "shelf_expired_holds") {
+ dojo.byId("clear_holds_launcher").onclick = function() {
+ if (confirm("Are you sure you're ready to clear the expired holds from the shelf?")) { /* XXX i18n */
+ do_clear_holds(cgi);
+ }
+ };
+ openils.Util.show("clear_holds_deck");
+ } else {
+ dojo.query("[only='shelf_expired_holds']").forEach(dojo.destroy);
+ do_pull_list(cgi);
+ }
+ }
+ dojo.addOnLoad(my_init);
+ </script>
+ </head>
+ <body class='tundra'>
+
+ <div style="width: 320px;"
+ dojoType="openils.widget.ProgressDialog"
+ jsId="progress_dialog"></div>
+ <div class="hide_me" id="no_results">No results</div>
+ <div class="hide_me" id="clear_holds_deck">
+ [ <a id="clear_holds_launcher"
+ href="javascript:void(0);">Clear expired holds</a> ]
+ <small><em id="clear_holds_set_label"></em></small>
+ </div>
+<!-- START OF TEMPLATE SECTION -->
+ <table>
+ <thead>
+ <tr>
+ <th only="shelf_expired_holds">Patron</th>
+ <th only="shelf_expired_holds">Action</th>
+ <th>Title</th>
+ <th>Author</th>
+ <th>Shelving Location</th>
+ <th>Call Number</th>
+ <th>Barcode</th>
+ </tr>
+ </thead>
+ <tbody id='target'>
+ </tbody>
+ <tbody id='template' class='hide_me'>
+ <tr>
+ <td only="shelf_expired_holds">${usr.display_name}</td>
+ <td only="shelf_expired_holds">${action}</td>
+ <td type='opac/slot-data' query='datafield[tag=245]'></td>
+ <td type='opac/slot-data' query='datafield[tag^=1]' limit='1'> </td>
+ <td>${current_copy.location.name}</td>
+ <td>${current_copy.call_number.label}</td>
+ <td>${current_copy.barcode}</td>
+ </tr>
+ </tbody>
+ </table>
+<!-- END OF TEMPLATE SECTION -->
+ </body>
+</html>
--- /dev/null
+dojo.require("dojo.cookie");
+dojo.require("dojox.xml.parser");
+dojo.require("openils.BibTemplate");
+dojo.require("openils.widget.ProgressDialog");
+
+var authtoken;
+var cgi;
+
+function do_pull_list() {
+ progress_dialog.show(true);
+
+ var any = false;
+
+ fieldmapper.standardRequest(
+ ['open-ils.circ','open-ils.circ.hold_pull_list.print.stream'],
+ { async : true,
+ params: [
+ authtoken, {
+ org_id : cgi.param('o'),
+ limit : cgi.param('limit'),
+ offset : cgi.param('offset'),
+ chunk_size : cgi.param('chunk_size'),
+ sort : sort_order
+ }
+ ],
+ onresponse : function (r) {
+ any = true;
+ dojo.forEach( openils.Util.readResponse(r), function (hold_fm) {
+
+ // hashify the hold
+ var hold = hold_fm.toHash(true);
+ hold.usr = hold_fm.usr().toHash(true);
+ hold.usr.card = hold_fm.usr().card().toHash(true);
+ hold.current_copy = hold_fm.current_copy().toHash(true);
+ hold.current_copy.location = hold_fm.current_copy().location().toHash(true);
+ hold.current_copy.call_number = hold_fm.current_copy().call_number().toHash(true);
+ hold.current_copy.call_number.record = hold_fm.current_copy().call_number().record().toHash(true);
+
+ // clone the template's html
+ var tr = dojo.clone(
+ dojo.query("tr", dojo.byId('template'))[0]
+ );
+ dojo.query("td:not([type])", tr).forEach(
+ function(td) {
+ td.innerHTML =
+ dojo.string.substitute(td.innerHTML, hold);
+ }
+ );
+
+ new openils.BibTemplate({
+ root : tr,
+ xml : dojox.xml.parser.parse(hold.current_copy.call_number.record.marc),
+ delay: false
+ });
+
+ dojo.place(tr, "target");
+ });
+ },
+ oncomplete : function () {
+ progress_dialog.hide();
+ if (any)
+ window.print();
+ else
+ alert(dojo.byId("no_results").innerHTML);
+ }
+ }
+ );
+}
+
+function place_by_sortkey(node, container) {
+ /*Don't use a forEach() or anything like that here. too slow.*/
+ var sortkey = dojo.attr(node, "sortkey");
+ for (var i = 0; i < container.childNodes.length; i++) {
+ var rover = container.childNodes[i];
+ if (rover.nodeType != 1) continue;
+ if (dojo.attr(rover, "sortkey") > sortkey) {
+ dojo.place(node, rover, "before");
+ return;
+ }
+ }
+ dojo.place(node, container, "last");
+}
+
+function hashify_fields(fields) {
+ var hold = {
+ "usr": {},
+ "current_copy": {
+ "barcode": fields.barcode,
+ "call_number": {
+ "label": fields.label,
+ "record": {"marc": fields.marc}
+ },
+ "location": {"name": fields.name}
+ }
+ };
+
+ if (fields.alias) {
+ hold.usr.display_name = fields.alias;
+ } else {
+ hold.usr.display_name = [
+ (fields.family_name ? fields.family_name : ""),
+ (fields.first_given_name ? fields.first_given_name : ""),
+ (fields.second_given_name ? fields.second_given_name : "")
+ ].join(" ");
+ }
+
+ ["first_given_name","second_given_name","family_name","alias"].forEach(
+ function(k) { hold.usr[k] = fields[k]; }
+ );
+
+ return hold;
+}
+
+function do_clear_holds() {
+ progress_dialog.show(true);
+
+ var launcher;
+ fieldmapper.standardRequest(
+ ["open-ils.circ", "open-ils.circ.hold.clear_shelf.process"], {
+ "async": true,
+ "params": [authtoken, cgi.param("o")],
+ "onresponse": function(r) {
+ if (r = openils.Util.readResponse(r)) {
+ if (r.cache_key) { /* complete */
+ launcher = dojo.byId("clear_holds_launcher");
+ launcher.innerHTML = "Re-fetch for Printing"; /* XXX i18n */
+ launcher.onclick =
+ function() { do_clear_holds_from_cache(r.cache_key); };
+ dojo.byId("clear_holds_set_label").innerHTML = r.cache_key;
+ } else if (r.maximum) {
+ progress_dialog.update(r);
+ }
+ }
+ },
+ "oncomplete": function() {
+ progress_dialog.hide();
+ if (launcher) launcher.onclick();
+ else alert(dojo.byId("no_results").innerHTML);
+ }
+ }
+ );
+}
+
+function do_clear_holds_from_cache(cache_key) {
+ progress_dialog.show(true);
+
+ var any = 0;
+ var target = dojo.byId("target");
+ dojo.empty(target);
+ var template = dojo.query("tr", dojo.byId("template"))[0];
+ fieldmapper.standardRequest(
+ ["open-ils.circ",
+ "open-ils.circ.hold.clear_shelf.get_cache"], {
+ "async": true,
+ "params": [authtoken, cache_key, cgi.param("chunk_size")],
+ "onresponse": function(r) {
+ dojo.forEach(
+ openils.Util.readResponse(r),
+ function(resp) {
+ if (resp.maximum) {
+ progress_dialog.update(resp);
+ return;
+ }
+
+ var hold = hashify_fields(resp.hold_details);
+ hold.action = resp.action;
+
+ var tr = dojo.clone(template);
+ any++;
+
+ dojo.query("td:not([type])", tr).forEach(
+ function(td) {
+ td.innerHTML =
+ dojo.string.substitute(td.innerHTML, hold);
+ }
+ );
+
+ new openils.BibTemplate({
+ "root": tr,
+ "xml": dojox.xml.parser.parse(
+ hold.current_copy.call_number.record.marc
+ ),
+ "delay": false
+ });
+
+ dojo.attr(tr, "sortkey", hold.usr.display_name);
+ place_by_sortkey(tr, target);
+ }
+ );
+ progress_dialog.update({"progress": any});
+ },
+ "oncomplete": function() {
+ progress_dialog.hide();
+ if (any)
+ window.print();
+ else
+ alert(dojo.byId("no_results").innerHTML);
+ }
+ }
+ );
+}
+
<!ENTITY conify.grp_tree.group_name.label "Group Name">
<!ENTITY conify.grp_tree.description.label "Description">
<!ENTITY conify.grp_tree.permission_interval.label "Permission Interval">
+ <!ENTITY conify.grp_tree.hold_priority.label "Hold Priority">
<!ENTITY conify.grp_tree.editing_permission.label "Editing Permission">
<!ENTITY conify.grp_tree.parent_group.label "Parent Group">
<!ENTITY conify.grp_tree.user_group.label "User Group">
<!ENTITY staff.cat.popup.edit.record.window.key "">
<!ENTITY staff.cat.popup.edit_record.tab "Edit Record (Tab)">
<!ENTITY staff.cat.popup.edit_record.window "Edit Record (Window)">
+<!ENTITY staff.cat.record_buckets.merge_records.merge_lead "Merge these records? (Select the 'lead' record first)">
+<!ENTITY staff.cat.record_buckets.merge_records.button.label "Merge">
+<!ENTITY staff.cat.record_buckets.merge_records.button.accesskey "M">
+<!ENTITY staff.cat.record_buckets.merge_records.cancel_button.label "Cancel">
+<!ENTITY staff.cat.record_buckets.merge_records.cancel_button.accesskey "C">
+<!ENTITY staff.cat.record_buckets.merge_records.lead "Lead Record?">
+<!ENTITY staff.cat.record_buckets.merge_records.remove_from_consideration "Remove from consideration?">
<!ENTITY staff.cat.search_advanced "Advanced">
<!ENTITY staff.cat.search_advanced.key "V">
<!ENTITY staff.cat.search_all "Keyword">
<!ENTITY staff.main.menu.cat.edit_user_buckets.label "Manage User Buckets">
<!ENTITY staff.main.menu.cat.key "a">
<!ENTITY staff.main.menu.cat.label "Cataloging">
+<!ENTITY staff.main.menu.cat.marc_batch_edit.label "MARC Batch Edit">
+<!ENTITY staff.main.menu.cat.marc_batch_edit.accesskey "E">
<!ENTITY staff.main.menu.cat.retrieve_last_record.accesskey "L">
<!ENTITY staff.main.menu.cat.retrieve_last_record.label "Retrieve Last Record">
<!ENTITY staff.main.menu.cat.search_tcn.accesskey "T">
<!ENTITY staff.server.admin.index.library_settings "Library Settings Editor">
<!ENTITY staff.server.admin.index.non_cataloged_types "Non-cataloged Types Editor">
<!ENTITY staff.server.admin.index.statistical_categories "Statistical Categories Editor">
+<!ENTITY staff.server.admin.index.expired_holds_shelf "Expired Holds Shelf Printable Listing">
<!ENTITY staff.server.admin.index.hold_pull_list "Pull List for Hold Requests">
<!ENTITY staff.server.admin.index.testing "(Testing)">
<!ENTITY staff.server.admin.index.hold_pull_list_classic "Pull List for Hold Requests (Classic)">
<!ENTITY staff.server.admin.index.external_text_editor.label "External Text Editor Command">
<!ENTITY staff.server.admin.index.external_text_editor.accesskey "x">
-<!ENTITY staff.server.admin.index.booking "Booking">
-<!ENTITY staff.server.admin.index.booking.reservation "Create/Cancel Reservations">
-<!ENTITY staff.server.admin.index.booking.pull_list "Pull List">
-<!ENTITY staff.server.admin.index.booking.capture "Capture">
-<!ENTITY staff.server.admin.index.booking.pickup "Pickup Reservations">
-<!ENTITY staff.server.admin.index.booking.return "Return Reservations">
-
-
<!ENTITY staff.server.admin.org_settings.title "Evergreen: Library Settings Editor">
<!-- This will be followed by the user's name -->
<!ENTITY staff.server.admin.org_settings.greeting "Welcome ">
<!ENTITY staff.cat.marcedit.validate.accesskey "V">
<!ENTITY staff.cat.marcedit.save-button.accesskey "d">
<!ENTITY staff.cat.marcedit.help.label "Help">
-<!ENTITY staff.cat.marcedit.swapEditor.label "Swap Editor Type">
+<!ENTITY staff.cat.marcedit.flatTextEditor.label "Flat-Text Editor">
+<!ENTITY staff.cat.marcedit.flatTextEditor.accesskey "">
<!ENTITY staff.cat.marcedit.help.accesskey "H">
<!ENTITY staff.cat.marcedit.caption.label "MARC Record">
<!ENTITY staff.cat.marcedit.toggleFFE.label "Fixed Fields -- Record type: ">
<!ENTITY staff.cat.record_buckets_overlay.box.label "Batch:">
<!ENTITY staff.cat.record_buckets_overlay.sel_opac.label "Show All in Catalog">
<!ENTITY staff.cat.record_buckets_overlay.transfer_title_holds.label "Transfer Title Holds">
-<!ENTITY staff.cat.record_buckets_overlay.transfer_title_holds.accesskey "Transfer Title Holds">
+<!ENTITY staff.cat.record_buckets_overlay.transfer_title_holds.accesskey "T">
+<!ENTITY staff.cat.record_buckets_overlay.marc_batch_edit.label "MARC Batch Edit">
+<!ENTITY staff.cat.record_buckets_overlay.marc_batch_edit.accesskey "">
<!ENTITY staff.cat.record_buckets_overlay.del_records.label "Delete All Records">
<!ENTITY staff.cat.record_buckets_overlay.merge_records.label "Merge All Records">
<!ENTITY staff.cat.record_buckets_overlay.export_records.label "Export All Records">
<!ENTITY staff.cat.z3950.menuitem.save_columns.label "Save List Configuration">
<!ENTITY staff.cat.z3950.marc_view.label "MARC View">
<!ENTITY staff.cat.z3950.marc_view.accesskey "V">
-<!ENTITY staff.cat.z3950.marc_import_overlay.label "MARC Editor for Overlay">
+<!ENTITY staff.cat.z3950.marc_editor.label "MARC Editor">
+<!ENTITY staff.cat.z3950.marc_editor.accesskey "E">
+<!ENTITY staff.cat.z3950.marc_import_overlay.label "Overlay">
<!ENTITY staff.cat.z3950.marc_import_overlay.accesskey "O">
-<!ENTITY staff.cat.z3950.result_message.marc_import.label "MARC Editor for Import">
+<!ENTITY staff.cat.z3950.result_message.marc_import.label "Import">
<!ENTITY staff.cat.z3950.result_message.marc_import.accesskey "I">
<!ENTITY staff.pat.barcode_entry.retrieve_patron.label "Retrieve Patron">
<!ENTITY staff.pat.barcode_entry.barcode.label "Barcode:">
<!ENTITY staff.patron.holds_overlay.print.accesskey "P">
<!ENTITY staff.patron.holds_overlay.print_full_pull_list.label "Print Full Pull List">
<!ENTITY staff.patron.holds_overlay.print_full_pull_list.accesskey "u">
+<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.label "Print Full Pull List (Alternate strategy)">
+<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.accesskey "y">
<!ENTITY staff.patron.holds_overlay.place_hold.label "Place Hold">
<!ENTITY staff.patron.holds_overlay.place_hold.accesskey "H">
<!ENTITY staff.patron.holds_overlay.show_cancelled_holds.label "Show Cancelled Holds">
name="serial_holdings_label"
class="result_table_title_cell hide_me"
type="opac/slot-data"
- query="datafield[tag=901] subfield[code=c]">
+ query="datafield[tag='901'] subfield[code='c']">
<td colspan="2">Issues Held: ${holdingsStatement}
<span class="hide_me" name="holdingsStatement" type="opac/template-value"><![CDATA[
if (fetchOrgSettingDefault(
'acqligad','acqliuad','acqlipad','acqphsm','acqlilad','acqedi','acqedim','acqdf','acqdfe','acqdfa','acqda','cnal',
'acqclt','acqclet','acqcl','acqcle','acqscl','acqscle','acqclp','acqclpa','acqlisum','acqft','acqftm','actsce','actscecm',
'jub','sdist','ssub','sstr','scap','bre','siss','act', 'acpl', 'ccm', 'aiit', 'atevdef', 'ath', 'atreact','atclean','atenv','atevparam','atcol','actsc','cit',
-'atval','crahp','crmf','crrf','crcd','cust','coust','cgf','czs','cbt','csp','brt','brsrc','bra','bram','brav','vaq','vbq','vqar','ccmm','ccmcmtm','citm','cifm','cvrfm']};
+'atval','crahp','crmf','crrf','crcd','cust','coust','cgf','czs','cbt','csp','brt','brsrc','bra','bram','brav','vaq','vbq','vqar','ccmm','ccmcmtm','citm','cifm','cvrfm','chmm']};
</script>
<script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/dojo/dojo.js"></script>
<script type="text/javascript" src="[% ctx.media_prefix %]/js/dojo/dojo/openils_dojo.js"></script>
[% WRAPPER 'default/base.tt2' %]
+[% ctx.page_title = "Purchase Order" %]
<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/common/base64.js"></script>
<script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/acq/po/view_po.js'></script>
<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/po/item_table.js"></script>
</tr>
- <tr fmclass='aua' fmfield='address_type' type='addr-template' required='show'/>
+ <tr fmclass='aua' fmfield='address_type' type='addr-template' required='required'/>
<tr fmclass='aua' fmfield='post_code' type='addr-template' required='required'/>
<tr fmclass='aua' fmfield='street1' type='addr-template' required='required'/>
<tr fmclass='aua' fmfield='street2' type='addr-template' required='show'/>
<script type="text/javascript">
SelfCheckManager.audioConfig = {
'login-success' : '',
- 'login-failure' : '[% ctx.media_prefix %]/audio/circ/question.wav',
- 'checkout-success' : '[% ctx.media_prefix %]/audio/circ/bonus.wav',
- 'checkout-failure' : '[% ctx.media_prefix %]/audio/circ/question.wav',
+ 'login-failure' : '[% ctx.media_prefix %]/audio/question.wav',
+ 'checkout-success' : '[% ctx.media_prefix %]/audio/bonus.wav',
+ 'checkout-failure' : '[% ctx.media_prefix %]/audio/question.wav',
}
</script>
<div dojoType="dijit.layout.ContentPane" layoutAlign="client" style='height:600px'>
<table jsId="edGrid"
dojoType="openils.widget.AutoGrid"
- fieldOrder="['owner', 'name', 'hook', 'active', 'delay', 'delay_field', 'group_field', 'validator', 'reactor']"
- suppressFields="['template', 'cleanup_failure', 'cleanup_success']"
+ fieldOrder="['owner', 'name', 'hook', 'active', 'delay', 'delay_field', 'group_field', 'reactor', 'validator']"
+ suppressFields="['usr_field', 'opt_in_setting', 'max_delay', 'template', 'cleanup_failure', 'cleanup_success']"
query="{id: '*'}"
fmClass='atevdef'
- defaultCellWidth='"auto"'
editStyle='pane'
showPaginator='true'
editOnEnter='true'>
<thead>
- <tr><th field='name' get='getEventDefNameLink' formatter='formatEventDefNameLink'/></tr>
+ <tr><th field='name' width='15%' get='getEventDefNameLink' formatter='formatEventDefNameLink'/></tr>
</thead>
</table>
</div>
<table jsId="hmGrid"
autoHeight='true'
dojoType="openils.widget.AutoGrid"
- fieldOrder="['id', 'user_home_ou', 'request_ou', 'pickup_ou', 'item_owning_ou', 'item_circ_ou', 'usr_grp', 'requestor_grp', 'circ_modifier']"
+ fieldOrder="['id', 'strict_ou_match', 'user_home_ou', 'request_ou', 'pickup_ou', 'item_owning_ou', 'item_circ_ou', 'requestor_grp', 'circ_modifier']"
+ suppressFields="['usr_grp']"
defaultCellWidth='"auto"'
query="{id: '*'}"
fmClass='chmm'
<div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'>
<div>Import Item Attribute Definitions</div>
<div>
- <button dojoType='dijit.form.Button' onClick='itemAttrGrid.showCreateDialog()'>New Definition</button>
+ <button dojoType='dijit.form.Button' onClick='itemAttrGrid.showCreatePane()'>New Definition</button>
<button dojoType='dijit.form.Button' onClick='itemAttrGrid.deleteSelected()'>Delete Selected</button>
</div>
</div>
break;
case 'actsc':
found = obj.network.simple_request('FM_ACTSC_RETRIEVE_VIA_PCRUD',[ ses(), { 'id' : { '=' : value } }]);
+ if (typeof found.ilsevent != 'undefined') throw(found);
+ found = found[0];
break;
default: return undefined; break;
}
if (ev.explicitOriginalTarget != node) return;
} else {
target = ev.target;
+ if (target == window) {
+ target = window.document.documentElement;
+ }
}
var filename = location.pathname.split('/')[ location.pathname.split('/').length - 1 ];
var base_key = 'oils_persist_' + String(location.hostname + '_' + filename + '_' + target.getAttribute('id')).replace('/','_','g') + '_' + base_key_suffix;
} else if ( attribute_list[j] == 'value' && ['textbox'].indexOf( target.nodeName ) > -1 ) {
value = target.value;
dump('\t' + value + ' <== .' + attribute_list[j] + '\n');
+ } else if ( attribute_list[j] == 'sizemode' && ['window'].indexOf( target.nodeName ) > -1 ) {
+ value = window.windowState;
+ dump('\t' + value + ' <== window.windowState, @' + attribute_list[j] + '\n');
+ } else if ( attribute_list[j] == 'height' && ['window'].indexOf( target.nodeName ) > -1 ) {
+ value = window.outerHeight;
+ dump('\t' + value + ' <== window.outerHeight, @' + attribute_list[j] + '\n');
+ } else if ( attribute_list[j] == 'width' && ['window'].indexOf( target.nodeName ) > -1 ) {
+ value = window.outerWidth;
+ dump('\t' + value + ' <== window.outerWidth, @' + attribute_list[j] + '\n');
} else {
dump('\t' + value + ' <== @' + attribute_list[j] + '\n');
}
prefs.setCharPref( key, value );
- // TODO: Need to add logic for window resizing, splitter repositioning, grippy state, etc.
+ // TODO: Need to add logic for splitter repositioning, grippy state, etc.
+ // NOTE: oils_persist_peers and oils_persist="width" on those peers can help with the elements adjacent to a splitter
}
if (target.hasAttribute('oils_persist_peers') && ! ev.cancelable) { // We abuse the .cancelable field on the oils_persist event to prevent looping
var peer_list = target.getAttribute('oils_persist_peers').split(' ');
} else if ( attribute_list[j] == 'value' && ['textbox'].indexOf( nodes[i].nodeName ) > -1 ) {
nodes[i].value = value;
dump('\t' + value + ' ==> .' + attribute_list[j] + '\n');
+ } else if ( attribute_list[j] == 'sizemode' && ['window'].indexOf( nodes[i].nodeName ) > -1 ) {
+ switch(value) {
+ case window.STATE_MAXIMIZED:
+ window.maximize();
+ break;
+ case window.STATE_MINIMIZED:
+ window.minimize();
+ break;
+ };
+ dump('\t' + value + ' ==> window.windowState, @' + attribute_list[j] + '\n');
+ } else if ( attribute_list[j] == 'height' && ['window'].indexOf( nodes[i].nodeName ) > -1 ) {
+ window.outerHeight = value;
+ dump('\t' + value + ' ==> window.outerHeight, @' + attribute_list[j] + '\n');
+ } else if ( attribute_list[j] == 'width' && ['window'].indexOf( nodes[i].nodeName ) > -1 ) {
+ window.outerWidth = value;
+ dump('\t' + value + ' ==> window.outerWidth, @' + attribute_list[j] + '\n');
} else {
nodes[i].setAttribute( attribute_list[j], value);
dump('\t' + value + ' ==> @' + attribute_list[j] + '\n');
false
);
} else {
+ var node = nodes[i];
var event_types = [];
- if (nodes[i].hasAttribute('oils_persist_events')) {
- var event_type_list = nodes[i].getAttribute('oils_persist_events').split(' ');
+ if (node.hasAttribute('oils_persist_events')) {
+ var event_type_list = node.getAttribute('oils_persist_events').split(' ');
for (var j = 0; j < event_type_list.length; j++) {
event_types.push( event_type_list[j] );
}
} else {
- if (nodes[i].nodeName == 'textbox') {
+ if (node.nodeName == 'textbox') {
event_types.push('change');
+ } else if (node.nodeName == 'window') {
+ event_types.push('resize');
+ node = window; // xul window is an element of window.document
} else {
event_types.push('command');
}
}
for (var j = 0; j < event_types.length; j++) {
- nodes[i].addEventListener(
+ node.addEventListener(
event_types[j],
- gen_event_handler(event_types[j],nodes[i]),
+ gen_event_handler(event_types[j],node),
false
);
}
- nodes[i].addEventListener(
+ node.addEventListener(
'oils_persist',
- gen_oils_persist_handler( base_key, nodes[i] ),
+ gen_oils_persist_handler( base_key, node ),
false
);
}
'close' : function () {
var obj = this;
obj.error.sdump('D_AUTH','auth.session.close()\n');
+ try {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ Components.classes["@mozilla.org/cookiemanager;1"]
+ .getService(Components.interfaces.nsICookieManager).removeAll();
+ } catch(E) {
+ dump('Error in auth/session.js, close(): ' + E + '\n');
+ }
if (obj.key) obj.network.request(
api.AUTH_DELETE.app,
api.AUTH_DELETE.method,
JSAN.use('util.window'); var win = new util.window();
win.open(
xulG.url_prefix(urls.XUL_SERIAL_SELECT_AOU),
- 'sel_bucket_win' + win.window_name_increment(),
+ '_blank',
'chrome,resizable,modal,centerscreen'
);
if (!g.data.create_mfhd_aou) {
JSAN.use('util.window'); var win = new util.window();
win.open(
xulG.url_prefix(urls.XUL_RECORD_BUCKETS_QUICK),
- 'sel_bucket_win' + win.window_name_increment(),
+ '_blank',
'chrome,resizable,modal,centerscreen',
{
record_ids: [ docid ]
<window id="offline_win" sizemode="maximized"
onload="try { my_init(); } catch(E) { alert(E); }"
+ windowtype="eg_offline"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
'browser' : '/opac/' + LOCALE + '/skin/default/xml/advanced.xml?nps=1',
'fieldmapper' : '/opac/common/js/fmall.js',
'xsl_marc2html' : '/opac/extras/xsl/oilsMARC21slim2HTML.xsl',
+ 'ac_jacket_small' : '/opac/extras/ac/jacket/small/',
+ 'ac_jacket_large' : '/opac/extras/ac/jacket/large/',
+ 'MARC_BATCH_EDIT' : '/opac/extras/merge_template/',
'AUDIO_good' : '/xul/server/skin/media/audio/bonus.wav',
'AUDIO_bad' : '/xul/server/skin/media/audio/question.wav',
'AUTHORITY_MANAGE' : '/eg/cat/authority/list',
'XUL_AUTH_SIMPLE' : '/xul/server/main/simple_auth.xul',
'XUL_BIB_BRIEF' : '/xul/server/cat/bib_brief.xul',
+ 'XUL_BIB_BRIEF_VERTICAL' : '/xul/server/cat/bib_brief_vertical.xul',
'XUL_BROWSER' : 'chrome://open_ils_staff_client/content/util/browser.xul',
'XUL_CHECKIN' : '/xul/server/circ/checkin.xul',
'XUL_BACKDATE' : '/xul/server/circ/backdate_post_checkin.xul',
var xulG;
var offlineStrings;
var authStrings;
+var openTabs = new Array();
+var tempWindow = null;
+var tempFocusWindow = null;
function grant_perms(url) {
var perms = "UniversalXPConnect UniversalPreferencesWrite UniversalBrowserWrite UniversalPreferencesRead UniversalBrowserRead UniversalFileRead";
);
};
+function new_tabs(aTabList, aContinue) {
+ if(aTabList != null) {
+ openTabs = openTabs.concat(aTabList);
+ }
+ if(G.data.session) { // Just add to the list of stuff to open unless we are logged in
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var targetwindow = null;
+ var focuswindow = null;
+ var focustab = {'focus' : true};
+ if(aContinue == true && tempWindow.closed == false) {
+ if(tempWindow.g == undefined || tempWindow.g.menu == undefined) {
+ setTimeout(
+ function() {
+ new_tabs(null, true);
+ }, 300);
+ return null;
+ }
+ targetwindow = tempWindow;
+ tempWindow = null;
+ focuswindow = tempFocusWindow;
+ tempFocusWindow = null;
+ focustab = {'nofocus' : true};
+ }
+ else if(tempWindow != null) { // In theory, we are waiting on a setTimeout
+ if(tempWindow.closed == true) // But someone closed our window?
+ {
+ tempWindow = null;
+ tempFocusWindow = null;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ var newTab;
+ var firstURL;
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+ getService(Components.interfaces.nsIWindowMediator);
+ // This may look out of place, but this is so we can continue this loop from down below
+opentabs:
+ while(openTabs.length > 0) {
+ newTab = openTabs.shift();
+ if(newTab == 'new' || newTab == 'init') {
+ if(newTab != 'init' && openTabs.length > 0 && openTabs[0] != 'new') {
+ firstURL = openTabs.shift();
+ if(firstURL != 'tab') { // 'new' followed by 'tab' should be equal to 'init' in functionality, this should do that
+ if(urls[firstURL]) {
+ firstURL = urls[firstURL];
+ }
+ firstURL = '&firstURL=' + window.escape(firstURL);
+ }
+ else {
+ firstURL = '';
+ }
+ }
+ else {
+ firstURL = '';
+ }
+ targetwindow = xulG.window.open(urls.XUL_MENU_FRAME
+ + '?server='+window.escape(G.data.server) + firstURL,
+ '_blank','chrome,resizable'
+ );
+ targetwindow.xulG = xulG;
+ if (focuswindow == null) {
+ focuswindow = targetwindow;
+ }
+ tempWindow = targetwindow;
+ tempFocusWindow = focuswindow;
+ setTimeout(
+ function() {
+ new_tabs(null, true);
+ }, 300);
+ return null;
+ }
+ else {
+ if(newTab == 'tab') {
+ newTab = null;
+ }
+ else if(urls[newTab]) {
+ newTab = urls[newTab];
+ }
+ if(targetwindow != null) { // Already have a previous target window? Use it first.
+ if(targetwindow.g.menu.new_tab(newTab,focustab,null)) {
+ focustab = {'nofocus' : true};
+ continue;
+ }
+ }
+ var enumerator = wm.getEnumerator('eg_menu');
+ while(enumerator.hasMoreElements()) {
+ targetwindow = enumerator.getNext();
+ if(targetwindow.g.menu.new_tab(newTab,focustab,null)) {
+ focustab = {'nofocus' : true};
+ if (focuswindow == null) {
+ focuswindow = targetwindow;
+ }
+ continue opentabs;
+ }
+ }
+ // No windows found to add the tab to? Make a new one.
+ if(newTab == null) { // Were we making a "default" tab?
+ openTabs.unshift('init'); // 'init' does that for us!
+ }
+ else {
+ openTabs.unshift('new',newTab);
+ }
+ }
+ }
+ if(focuswindow != null) {
+ focuswindow.focus();
+ }
+ }
+}
+
function main_init() {
dump('entering main_init()\n');
try {
clear_the_cache();
+ if("arguments" in window && window.arguments.length > 0 && window.arguments[0].wrappedJSObject != undefined && window.arguments[0].wrappedJSObject.openTabs != undefined) {
+ openTabs = openTabs.concat(window.arguments[0].wrappedJSObject.openTabs);
+ }
// Now we can safely load the strings without the cache getting wiped
offlineStrings = document.getElementById('offlineStrings');
'command',
function() {
if (G.data.session) {
- try {
- //G.data_xul.g.open_menu();
- netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
- var mframe = xulG.window.open(( String(urls.XUL_MENU_FRAME).match(/^chrome:/) ? '' : G.data.server ) + urls.XUL_MENU_FRAME
- + '?server='+window.escape(G.data.server),
- 'main'+xulG.window.window_name_increment(),'chrome,resizable'
- );
- mframe.xulG = xulG;
- } catch(E) { alert(E); }
+ new_tabs(Array('new'), null, null);
} else {
alert ( offlineStrings.getString('main.new_window_btn.login_first_warning') );
}
<window id="main_win"
onload="try { main_init(); } catch(E) { alert(E); }"
onunload="try { G.auth.logoff(); } catch(E) { alert(E); }"
- title="&staff.auth.title;"
+ title="&staff.auth.title;" persist="width height sizemode"
width="640" height="480" windowtype="eg_main"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
},
false
);
-
- if (xulG.pref.getBoolPref('open-ils.disable_accesskeys_on_tabs')) {
- var tabs = document.getElementById('main_tabs');
- for (var i = 0; i < tabs.childNodes.length; i++) {
- tabs.childNodes[i].setAttribute('accesskey','');
- }
- }
-
- if (xulG.pref.getBoolPref('open-ils.enable_join_tabs')) {
- document.getElementById('join_tabs_menuitem_vertical').hidden = false;
- document.getElementById('join_tabs_menuitem_horizontal').hidden = false;
- }
}
main.menu.prototype = {
'cmd_new_window' : [
['oncommand'],
function() {
- obj.data.stash_retrieve();
- var mframe = obj.window.open(
- obj.url_prefix(urls.XUL_MENU_FRAME)
- + '?server='+window.escape(urls.remote),
- 'main' + obj.window.window_name_increment(),
- 'chrome,resizable');
- netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
- mframe.xulG = xulG;
- /* This window should get its own objects for these */
- delete mframe.xulG['_data'];
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+ getService(Components.interfaces.nsIWindowMediator);
+ wm.getMostRecentWindow('eg_main').new_tabs(Array('new'));
}
],
'cmd_new_tab' : [
['oncommand'],
- function() { obj.new_tab(null,{'focus':true},null); }
- ],
- 'cmd_join_tabs_vertical' : [
- ['oncommand'],
- function() { obj.join_tabs({'orient':'vertical'}); }
- ],
- 'cmd_join_tabs_horizontal' : [
- ['oncommand'],
- function() { obj.join_tabs({'orient':'horizontal'}); }
+ function() {
+ if (obj.new_tab(null,{'focus':true},null) == false)
+ {
+ if(window.confirm(offlineStrings.getString('menu.new_tab.max_tab_dialog')))
+ {
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+ getService(Components.interfaces.nsIWindowMediator);
+ wm.getMostRecentWindow('eg_main').new_tabs(Array('tab'));
+ }
+ }
+ }
],
'cmd_close_tab' : [
['oncommand'],
}
],
+ 'cmd_marc_batch_edit' : [
+ ['oncommand'],
+ function() {
+ obj.set_tab(
+ obj.url_prefix(urls.MARC_BATCH_EDIT),{
+ 'tab_name' : offlineStrings.getString('menu.cmd_marc_batch_edit.tab')
+ },
+ {}
+ );
+ }
+ ],
+
/* Admin menu */
'cmd_change_session' : [
['oncommand'],
}
}
],
-
};
JSAN.use('util.controller');
obj.controller.init( { 'window_knows_me_by' : 'g.menu.controller', 'control_map' : cmd_map } );
obj.controller.view.tabbox = window.document.getElementById('main_tabbox');
- obj.controller.view.tabs = obj.controller.view.tabbox.firstChild;
- obj.controller.view.panels = obj.controller.view.tabbox.lastChild;
-
- obj.new_tab(null,{'focus':true},null);
-
- obj.init_tab_focus_handlers();
+ // Despite what the docs say:
+ // The "tabs" element need not be the first child
+ // The "panels" element need not be the second/last
+ // Nor need they be the only ones there.
+ // Thus, use the IDs for robustness.
+ obj.controller.view.tabs = window.document.getElementById('main_tabs');
+ obj.controller.view.panels = window.document.getElementById('main_panels');
+ obj.controller.view.tabscroller = window.document.getElementById('main_tabs_scrollbox');
+ if(params['firstURL']) {
+ obj.new_tab(params['firstURL'],{'focus':true},null);
+ }
+ else {
+ obj.new_tab(null,{'focus':true},null);
+ }
},
'spawn_search' : function(s) {
obj.new_patron_tab( {}, { 'doit' : 1, 'query' : js2JSON(s) } );
},
- 'init_tab_focus_handlers' : function() {
- var obj = this;
- for (var i = 0; i < obj.controller.view.tabs.childNodes.length; i++) {
- var tab = obj.controller.view.tabs.childNodes[i];
- var panel = obj.controller.view.panels.childNodes[i];
- tab.addEventListener(
- 'command',
- function(p) {
- return function() {
- try {
- netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
- if (p
- && p.firstChild
- && ( p.firstChild.nodeName == 'iframe' || p.firstChild.nodeName == 'browser' )
- && p.firstChild.contentWindow
- ) {
- var cw = p.firstChild.contentWindow;
- var help_params = {
- 'protocol' : cw.location.protocol,
- 'hostname' : cw.location.hostname,
- 'port' : cw.location.port,
- 'pathname' : cw.location.pathname,
- 'src' : ''
- };
- obj.set_help_context(help_params);
- if (typeof cw.default_focus == 'function') {
- cw.default_focus();
- }
- }
- } catch(E) {
- obj.error.sdump('D_ERROR','init_tab_focus_handler: ' + js2JSON(E));
- }
- }
- }(panel),
- false
- );
- }
- },
-
- // We keep a reference to content_params fed to tabs, so if we manipulate the DOM (say, via join_tabs),
- // we can re-inject the content_params into the content if needed. We have to watch out for memory leaks
- // doing this.
- 'preserved_content_params' : {},
-
'close_all_tabs' : function() {
var obj = this;
try {
var count = obj.controller.view.tabs.childNodes.length;
- for (var i = 0; i < count; i++) obj.close_tab();
+ for (var i = 1; i < count; i++) obj.close_tab();
setTimeout( function(){ obj.controller.view.tabs.firstChild.focus(); }, 0);
} catch(E) {
obj.error.standard_unexpected_error_alert(offlineStrings.getString('menu.close_all_tabs.error'),E);
'close_tab' : function (specific_idx) {
var idx = specific_idx || this.controller.view.tabs.selectedIndex;
- var tab = this.controller.view.tabs.childNodes[idx];
var panel = this.controller.view.panels.childNodes[ idx ];
- while ( panel.lastChild ) panel.removeChild( panel.lastChild );
- if (idx == 0) {
- try {
- this.controller.view.tabs.advanceSelectedTab(+1);
- } catch(E) {
- this.error.sdump('D_TAB','failed tabs.advanceSelectedTab(+1):'+js2JSON(E) + '\n');
- try {
- this.controller.view.tabs.advanceSelectedTab(-1);
- } catch(E) {
- this.error.sdump('D_TAB','failed again tabs.advanceSelectedTab(-1):'+
- js2JSON(E) + '\n');
- }
- }
- } else {
- try {
- this.controller.view.tabs.advanceSelectedTab(-1);
- } catch(E) {
- this.error.sdump('D_TAB','failed tabs.advanceSelectedTab(-1):'+js2JSON(E) + '\n');
- try {
- this.controller.view.tabs.advanceSelectedTab(+1);
- } catch(E) {
- this.error.sdump('D_TAB','failed again tabs.advanceSelectedTab(+1):'+
- js2JSON(E) + '\n');
- }
- }
-
+ this.controller.view.tabs.removeItemAt(idx);
+ this.controller.view.panels.removeChild(panel);
+ if(this.controller.view.tabs.childNodes.length > idx) {
+ this.controller.view.tabbox.selectedIndex = idx;
}
-
- this.error.sdump('D_TAB','\tnew tabbox.selectedIndex = ' + this.controller.view.tabbox.selectedIndex + '\n');
-
- this.controller.view.tabs.childNodes[ idx ].hidden = true;
- this.error.sdump('D_TAB','tabs.childNodes[ ' + idx + ' ].hidden = true;\n');
-
- // Make sure we keep at least one tab open.
- var tab_flag = true;
- for (var i = 0; i < this.controller.view.tabs.childNodes.length; i++) {
- var tab = this.controller.view.tabs.childNodes[i];
- if (!tab.hidden)
- tab_flag = false;
+ else {
+ this.controller.view.tabbox.selectedIndex = idx - 1;
}
- if (tab_flag) {
- this.controller.view.tabs.selectedIndex = 0;
+ this.controller.view.tabscroller.ensureElementIsVisible(this.controller.view.tabs.selectedItem);
+ this.update_all_tab_names();
+ // Make sure we keep at least one tab open.
+ if(this.controller.view.tabs.childNodes.length == 1) {
this.new_tab();
}
},
-
- 'join_tabs' : function(params) {
- try {
- if (!params) { params = {}; }
- if (!params.orient) { params.orient = 'horizontal'; }
-
- var left_idx = params.specific_idx || this.controller.view.tabs.selectedIndex;
- var left_tab = this.controller.view.tabs.childNodes[left_idx];
- var left_panel = this.controller.view.panels.childNodes[ left_idx ];
-
- // Find next not-hidden tab
- var right_idx;
- for (var i = left_idx + 1; i<this.controller.view.tabs.childNodes.length; i++) {
- var tab = this.controller.view.tabs.childNodes[i];
- if (!tab.hidden && !right_idx) {
- right_idx = i;
- }
+
+ 'update_all_tab_names' : function() {
+ var doAccessKeys = !xulG.pref.getBoolPref('open-ils.disable_accesskeys_on_tabs');
+ for(var i = 1; i < this.controller.view.tabs.childNodes.length; ++i) {
+ var tab = this.controller.view.tabs.childNodes[i];
+ tab.curindex = i;
+ tab.label = i + ' ' + tab.origlabel;
+ if(doAccessKeys && offlineStrings.testString('menu.tab' + i + '.accesskey')) {
+ tab.accessKey = offlineStrings.getString('menu.tab' + i + '.accesskey');
}
- if (!right_idx) { return; }
-
- // Grab the content
- var right_tab = this.controller.view.tabs.childNodes[right_idx];
- var right_panel = this.controller.view.panels.childNodes[ right_idx ];
-
- var left_content = left_panel.removeChild( left_panel.firstChild );
- var right_content = right_panel.removeChild( right_panel.firstChild );
-
- // Create a wrapper and shuffle the content
- var box = params.orient == 'vertical' ? document.createElement('vbox') : document.createElement('hbox');
- box.setAttribute('flex',1);
- left_panel.appendChild(box);
- box.appendChild(left_content);
- var splitter = document.createElement('splitter');
- splitter.appendChild( document.createElement('grippy') );
- box.appendChild(splitter);
- box.appendChild(right_content);
-
- right_tab.hidden = true;
- // FIXME: if we really want to combine labels for joined tabs, need to handle the cases where the content dynamically set their tab
- // labels with xulG.set_tab_name
- left_tab.setAttribute('unadornedlabel', left_tab.getAttribute('unadornedlabel') + ' / ' + right_tab.getAttribute('unadornedlabel'));
- left_tab.setAttribute('label', left_tab.getAttribute('label') + ' / ' + right_tab.getAttribute('unadornedlabel'));
-
- // Re-apply content params, etc.
- var left_params = this.preserved_content_params[ left_idx ];
- var right_params = this.preserved_content_params[ right_idx ];
- this.preserved_content_params[ left_idx ] = function() {
- try {
- left_params();
- right_params();
- } catch(E) {
- alert('Error re-applying content params after join_tabs');
- }
- };
- this.preserved_content_params[ left_idx ]();
-
- } catch(E) {
- alert('Error in menu.js with join_tabs(): ' + E);
}
},
- 'find_free_tab' : function() {
- var last_not_hidden = -1;
- for (var i = 0; i<this.controller.view.tabs.childNodes.length; i++) {
- var tab = this.controller.view.tabs.childNodes[i];
- if (!tab.hidden)
- last_not_hidden = i;
+ 'new_tab' : function(url,params,content_params) {
+ var obj = this;
+ var max_tabs = 0;
+ try {
+ var max_tabs = xulG.pref.getIntPref('open-ils.window_max_tabs') || max_tabs;
}
- if (last_not_hidden == this.controller.view.tabs.childNodes.length - 1)
- last_not_hidden = -1;
- // If the one next to last_not_hidden is hidden, we want it.
- // Basically, we fill in tabs after existing tabs for as
- // long as possible.
- var idx = last_not_hidden + 1;
- var candidate = this.controller.view.tabs.childNodes[ idx ];
- if (candidate.hidden)
- return idx;
- // Alright, find the first hidden then
- for (var i = 0; i<this.controller.view.tabs.childNodes.length; i++) {
- var tab = this.controller.view.tabs.childNodes[i];
- if (tab.hidden)
- return i;
+ catch (e) {}
+ if(max_tabs > 0 && this.controller.view.tabs.childNodes.length > max_tabs) return false;
+ var tab = this.w.document.createElement('tab');
+ var panel = this.w.document.createElement('tabpanel');
+ var tabscroller = this.controller.view.tabscroller;
+ this.controller.view.tabs.appendChild(tab);
+ this.controller.view.panels.appendChild(panel);
+ tab.curindex = this.controller.view.tabs.childNodes.length - 1;
+ if(!xulG.pref.getBoolPref('open-ils.disable_accesskeys_on_tabs')) {
+ if(offlineStrings.testString('menu.tab' + tab.curindex + '.accesskey')) {
+ tab.accessKey = offlineStrings.getString('menu.tab' + tab.curindex + '.accesskey');
+ }
}
- return -1;
- },
-
- 'new_tab' : function(url,params,content_params) {
- var tc = this.find_free_tab();
- if (tc == -1) { return null; } // 9 tabs max
- var tab = this.controller.view.tabs.childNodes[ tc ];
- tab.hidden = false;
+ var tabs = this.controller.view.tabs;
+ tab.addEventListener(
+ 'command',
+ function() {
+ try {
+ tabscroller.ensureElementIsVisible(tab);
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ if (panel
+ && panel.firstChild
+ && ( panel.firstChild.nodeName == 'iframe' || panel.firstChild.nodeName == 'browser' )
+ && panel.firstChild.contentWindow
+ ) {
+ var cw = panel.firstChild.contentWindow;
+ var help_params = {
+ 'protocol' : cw.location.protocol,
+ 'hostname' : cw.location.hostname,
+ 'port' : cw.location.port,
+ 'pathname' : cw.location.pathname,
+ 'src' : ''
+ };
+ obj.set_help_context(help_params);
+ if (typeof cw.default_focus == 'function') {
+ cw.default_focus();
+ }
+ }
+ } catch(E) {
+ obj.error.sdump('D_ERROR','init_tab_focus_handler: ' + js2JSON(E));
+ }
+ }
+ ,
+ false
+ );
if (!content_params) content_params = {};
if (!params) params = {};
if (!params.tab_name) params.tab_name = offlineStrings.getString('menu.new_tab.tab');
if (!params.nofocus) params.focus = true; /* make focus the default */
try {
- if (params.focus) this.controller.view.tabs.selectedIndex = tc;
- params.index = tc;
+ if (params.focus) {
+ this.controller.view.tabs.selectedItem = tab;
+ tabscroller.ensureElementIsVisible(tab);
+ }
+ params.index = tab.curindex;
this.set_tab(url,params,content_params);
+ return true;
} catch(E) {
this.error.sdump('D_ERROR',E);
+ return false;
}
},
if (params.src) { help_btn.setAttribute('src', params.src); }
}
},
- 'augment_content_params' : function(idx,tab,params,content_params) {
+ 'set_tab' : function(url,params,content_params) {
var obj = this;
+ if (!url) url = '/xul/server/';
+ if (!url.match(/:\/\//) && !url.match(/^data:/)) url = urls.remote + url;
+ if (!params) params = {};
+ if (!content_params) content_params = {};
+ var idx = this.controller.view.tabs.selectedIndex;
+ if (params && typeof params.index != 'undefined') idx = params.index;
+ var tab = this.controller.view.tabs.childNodes[ idx ];
+ if (params.focus) tab.focus();
+ var panel = this.controller.view.panels.childNodes[ idx ];
+ while ( panel.lastChild ) panel.removeChild( panel.lastChild );
+
content_params.new_tab = function(a,b,c) { return obj.new_tab(a,b,c); };
content_params.set_tab = function(a,b,c) { return obj.set_tab(a,b,c); };
content_params.close_tab = function() { return obj.close_tab(); };
content_params.volume_item_creator = function(a) { return obj.volume_item_creator(a); };
content_params.get_new_session = function(a) { return obj.get_new_session(a); };
content_params.holdings_maintenance_tab = function(a,b,c) { return obj.holdings_maintenance_tab(a,b,c); };
- content_params.set_tab_name = function(name) { tab.setAttribute('unadornedlabel',name); tab.setAttribute('label',(idx + 1) + ' ' + name); };
+ content_params.set_tab_name = function(name) { tab.label = tab.curindex + ' ' + name; tab.origlabel = name; };
content_params.set_help_context = function(params) { return obj.set_help_context(params); };
content_params.open_chrome_window = function(a,b,c) { return xulG.window.open(a,b,c); };
content_params.url_prefix = function(url) { return obj.url_prefix(url); };
};
content_params.chrome_xulG = xulG;
content_params._data = xulG._data;
-
- return content_params;
- },
- 'set_tab' : function(url,params,content_params) {
- var obj = this;
- if (!url) url = '/xul/server/';
- if (!url.match(/:\/\//) && !url.match(/^data:/)) url = urls.remote + url;
- if (!params) params = {};
- if (!content_params) content_params = {};
- var idx = this.controller.view.tabs.selectedIndex;
- if (obj.preserved_content_params[idx]) { delete obj.preserved_content_params[ idx ]; }
- if (params && typeof params.index != 'undefined') idx = params.index;
- var tab = this.controller.view.tabs.childNodes[ idx ];
- if (params.focus) tab.focus();
- var panel = this.controller.view.panels.childNodes[ idx ];
- while ( panel.lastChild ) panel.removeChild( panel.lastChild );
-
- content_params = obj.augment_content_params(idx,tab,params,content_params);
if (params && params.tab_name) content_params.set_tab_name( params.tab_name );
var frame;
'passthru_content_params' : content_params,
}
);
- obj.preserved_content_params[ idx ] = function() {
- b.passthru_content_params = content_params;
- }
} catch(E) {
alert(E);
}
panel.appendChild(frame);
dump('creating iframe with src = ' + url + '\n');
frame.setAttribute('src',url);
- obj.preserved_content_params[ idx ] = function() {
- try {
- netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
- var cw = frame.contentWindow;
- if (typeof cw.wrappedJSObject != 'undefined') cw = cw.wrappedJSObject;
- cw.IAMXUL = true;
- cw.xulG = content_params;
- cw.addEventListener(
- 'load',
- function() {
- try {
- if (typeof cw.help_context_set_locally == 'undefined') {
- var help_params = {
- 'protocol' : cw.location.protocol,
- 'hostname' : cw.location.hostname,
- 'port' : cw.location.port,
- 'pathname' : cw.location.pathname,
- 'src' : ''
- };
- obj.set_help_context(help_params);
- } else if (typeof cw.default_focus == 'function') {
- cw.default_focus();
- }
- } catch(E) {
- obj.error.sdump('D_ERROR', 'main.menu, set_tab, onload: ' + E);
+ try {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var cw = frame.contentWindow;
+ if (typeof cw.wrappedJSObject != 'undefined') cw = cw.wrappedJSObject;
+ cw.IAMXUL = true;
+ cw.xulG = content_params;
+ cw.addEventListener(
+ 'load',
+ function() {
+ try {
+ if (typeof cw.help_context_set_locally == 'undefined') {
+ var help_params = {
+ 'protocol' : cw.location.protocol,
+ 'hostname' : cw.location.hostname,
+ 'port' : cw.location.port,
+ 'pathname' : cw.location.pathname,
+ 'src' : ''
+ };
+ obj.set_help_context(help_params);
+ } else if (typeof cw.default_focus == 'function') {
+ cw.default_focus();
}
- },
- false
- );
- } catch(E) {
- this.error.sdump('D_ERROR', 'main.menu: ' + E);
- }
- };
- obj.preserved_content_params[ idx ]();
+ } catch(E) {
+ obj.error.sdump('D_ERROR', 'main.menu, set_tab, onload: ' + E);
+ }
+ },
+ false
+ );
+ } catch(E) {
+ this.error.sdump('D_ERROR', 'main.menu: ' + E);
+ }
}
} catch(E) {
this.error.sdump('D_ERROR', 'main.menu:2: ' + E);
onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
orient="vertical" width="1024" height="740"
sizemode="maximized" persist="width height sizemode" title="&staff.main.menu.title;"
+ windowtype="eg_menu"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
JSAN.use('main.menu'); g.menu = new main.menu();
g.menu.init( {
'server' : g.cgi.param('server'),
+ 'firstURL' : g.cgi.param('firstURL'),
} );
JSAN.use('util.window'); g.window = new util.window();
<command id="cmd_new_tab" key="new-tab-key" />
<command id="cmd_close_tab" key="close-tab-key" />
<command id="cmd_close_all_tabs" key="close-all-tabs-key" />
- <command id="cmd_join_tabs_vertical" label="&staff.main.menu.file.join_tabs_vertical.label;" accesskey="&staff.main.menu.file.join_tabs_vertical.accesskey;" />
- <command id="cmd_join_tabs_horizontal" label="&staff.main.menu.file.join_tabs_horizontal.label;" accesskey="&staff.main.menu.file.join_tabs_horizontal.accesskey;" />
<command id="cmd_shutdown" />
<command id="cmd_edit_copy_buckets" />
<command id="cmd_print_list_template_edit" />
<command id="cmd_z39_50_import" />
<command id="cmd_create_new_marc_book" />
+ <command id="cmd_marc_batch_edit" label="&staff.main.menu.cat.marc_batch_edit.label;" accesskey="&staff.main.menu.cat.marc_batch_edit.accesskey;"/>
<command id="cmd_replace_barcode" />
<command id="cmd_reprint" />
<command id="cmd_retrieve_last_patron" />
<menupopup id="main.menu.file.popup">
<menuitem label="&staff.main.menu.file.new.label;" accesskey="&staff.main.menu.file.new.accesskey;" key="new-window-key" command="cmd_new_window"/>
<menuitem label="&staff.main.menu.file.new_tab.label;" accesskey="&staff.main.menu.file.new_tab.accesskey;" key="new-tab-key" command="cmd_new_tab"/>
- <menuitem id="join_tabs_menuitem_horizontal" hidden="true" command="cmd_join_tabs_horizontal"/>
- <menuitem id="join_tabs_menuitem_vertical" hidden="true" command="cmd_join_tabs_vertical"/>
<menuseparator />
<menuitem label="&staff.main.menu.file.close_tab.label;" accesskey="&staff.main.menu.file.close_tab.accesskey;" oldaccesskey="&staff.main.menu.file.close_tab.key;" key="close-tab-key" command="cmd_close_tab"/>
<menuitem label="&staff.main.menu.tabs.close;" accesskey="&staff.main.menu.tabs.close.accesskey;" key="close-all-tabs-key" command="cmd_close_all_tabs"/>
<menuitem label="&staff.main.menu.cat.create_marc.label;" accesskey="&staff.main.menu.cat.create_marc.accesskey;" command="cmd_create_marc"/>
<menuitem label="&staff.main.menu.cat.z39_50_import.label;" accesskey="&staff.main.menu.cat.z39_50_import.accesskey;" command="cmd_z39_50_import"/>
<menuitem label="&staff.main.menu.cat.vandelay.label;" command="cmd_open_vandelay"/>
+ <menuitem command="cmd_marc_batch_edit"/>
<menuseparator />
<menuitem label="&staff.main.menu.replace_barcode.label;" command="cmd_replace_barcode"/>
<menuitem label="&staff.main.menu.cat.retrieve_last_record.label;" accesskey="&staff.main.menu.cat.retrieve_last_record.accesskey;" command="cmd_retrieve_last_record" key="retrieve_last_record_key"/>
<box id="menu_frame_main" flex="1" orient="vertical">
<toolbox id="main_toolbox"/>
<tabbox id="main_tabbox" flex="1" eventnode="window" handleCtrlTab="true">
- <tabs id="main_tabs" closebutton="true" onclosetab="g.menu.close_tab()">
- <tab id="tab_1" accesskey="&staff.chrome.menu_frame_overlay.tab1.accesskey;" label="&staff.chrome.menu_frame_overlay.tab1.label;" hidden="true" />
- <tab id="tab_2" accesskey="&staff.chrome.menu_frame_overlay.tab2.accesskey;" label="&staff.chrome.menu_frame_overlay.tab2.label;" hidden="true" />
- <tab id="tab_3" accesskey="&staff.chrome.menu_frame_overlay.tab3.accesskey;" label="&staff.chrome.menu_frame_overlay.tab3.label;" hidden="true" />
- <tab id="tab_4" accesskey="&staff.chrome.menu_frame_overlay.tab4.accesskey;" label="&staff.chrome.menu_frame_overlay.tab4.label;" hidden="true" />
- <tab id="tab_5" accesskey="&staff.chrome.menu_frame_overlay.tab5.accesskey;" label="&staff.chrome.menu_frame_overlay.tab5.label;" hidden="true" />
- <tab id="tab_6" accesskey="&staff.chrome.menu_frame_overlay.tab6.accesskey;" label="&staff.chrome.menu_frame_overlay.tab6.label;" hidden="true" />
- <tab id="tab_7" accesskey="&staff.chrome.menu_frame_overlay.tab7.accesskey;" label="&staff.chrome.menu_frame_overlay.tab7.label;" hidden="true" />
- <tab id="tab_8" accesskey="&staff.chrome.menu_frame_overlay.tab8.accesskey;" label="&staff.chrome.menu_frame_overlay.tab8.label;" hidden="true" />
- <tab id="tab_9" accesskey="&staff.chrome.menu_frame_overlay.tab9.accesskey;" label="&staff.chrome.menu_frame_overlay.tab9.label;" hidden="true" />
- </tabs>
+ <hbox>
+ <arrowscrollbox orient="horizontal" id="main_tabs_scrollbox" flex="2">
+ <tabs id="main_tabs">
+ <tab hidden="true" />
+ </tabs>
+ </arrowscrollbox>
+ <toolbarbutton id="main_tabs_closebutton" class="tabs-closebutton close-button" oncommand="g.menu.close_tab()" />
+ </hbox>
<tabpanels id="main_panels" flex="1">
- <tabpanel id="panel_1"><label value="panel_1"/></tabpanel>
- <tabpanel id="panel_2"><label value="panel_2"/></tabpanel>
- <tabpanel id="panel_3"><label value="panel_3"/></tabpanel>
- <tabpanel id="panel_4"><label value="panel_4"/></tabpanel>
- <tabpanel id="panel_5"><label value="panel_5"/></tabpanel>
- <tabpanel id="panel_6"><label value="panel_6"/></tabpanel>
- <tabpanel id="panel_7"><label value="panel_7"/></tabpanel>
- <tabpanel id="panel_8"><label value="panel_8"/></tabpanel>
- <tabpanel id="panel_9"><label value="panel_9"/></tabpanel>
+ <tabpanel />
</tabpanels>
</tabbox>
<statusbar>
<menu id="main.menu.admin" />
<menu id="main.menu.help" />
</menubar>
- <toolbar id="main_toolbar" hidden="true" class="chromeclass-toolbar">
+ <toolbar id="main_toolbar" hidden="true">
<toolbarbutton id="tb_checkout"
command="cmd_circ_checkout"
image="chrome://open_ils_staff_client/skin/media/images/Arrow-rightup-small.png"
dump('entering util/deck.js\n');
if (typeof util == 'undefined') util = {};
-util.deck = function (id) {
+util.deck = function (id_or_node) {
- this.node = document.getElementById(id);
+ this.node = typeof id_or_node == 'object' ? id_or_node : document.getElementById(id_or_node);
JSAN.use('util.error'); this.error = new util.error();
if (!this.node) {
- var error = 'util.deck: Could not find element ' + id;
+ var error = 'util.deck: Could not find element ' + id_or_node;
this.error.sdump('D_ERROR',error);
throw(error);
}
if (this.node.nodeName != 'deck') {
- var error = 'util.deck: ' + id + 'is not a deck' + "\nIt's a " + this.node.nodeName;
+ var error = 'util.deck: ' + id_or_node + 'is not a deck' + "\nIt's a " + this.node.nodeName;
this.error.sdump('D_ERROR',error);
throw(error);
}
}
/* local file will trump remote file if allowed, so save ourselves an http request if this is the case */
if (obj.data.hash.aous['url.remote_column_settings'] && ! my_cols ) {
- var x = new XMLHttpRequest();
- var url = obj.data.hash.aous['url.remote_column_settings'] + '/tree_columns_for_' + window.escape(id);
- x.open("GET", url, false);
- x.send(null);
- if (x.status == 200) {
- my_cols = JSON2js( x.responseText );
+ try {
+ var x = new XMLHttpRequest();
+ var url = obj.data.hash.aous['url.remote_column_settings'] + '/tree_columns_for_' + window.escape(id);
+ x.open("GET", url, false);
+ x.send(null);
+ if (x.status == 200) {
+ my_cols = JSON2js( x.responseText );
+ }
+ } catch(E) {
+ // This can happen in the offline interface if you logged in previously and url.remote_column_settings is set.
+ // 1) You may be really "offline" now
+ // 2) the URL may just be a path component without a hostname (ie "/xul/column_settings/"), which won't work
+ // when appended to chrome://open_ils_staff_client/
+ dump('Error retrieving column settings from ' + url + ': ' + E + '\n');
}
}
if (
(typeof result.ilsevent != 'undefined') &&
(
- (override_params.overridable_events.indexOf( result.ilsevent == null ? null : Number(result.ilsevent) ) != -1) ||
+ (override_params.overridable_events.indexOf( result.ilsevent == null || result.ilsevent == '' ? null : Number(result.ilsevent) ) != -1) ||
(override_params.overridable_events.indexOf( result.textcode ) != -1)
)
) {
if (
(result[i].ilsevent != 'undefined') &&
(
- (override_params.overridable_events.indexOf( result[i].ilsevent == null ? null : Number(result[i].ilsevent) ) != -1) ||
+ (override_params.overridable_events.indexOf( result[i].ilsevent == null || result.ilsevent == '' ? null : Number(result[i].ilsevent) ) != -1) ||
(override_params.overridable_events.indexOf( result[i].textcode ) != -1)
)
) {
var prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces['nsIPrefBranch']);
var key = 'oils.printer.external.cmd.' + this.context;
var has_key = prefs.prefHasUserValue(key);
+ if(!has_key && this.context != 'default') {
+ key = 'oils.printer.external.cmd.default';
+ has_key = prefs.prefHasUserValue(key);
+ }
this.oils_printer_external_cmd = has_key ? prefs.getCharPref(key) : '';
return this;
document.getElementById('browser_print').hidden = false;
}
+ if (xul_param('show_toolbar')) {
+ document.getElementById('browser_toolbar').hidden = xul_param('show_toolbar');
+ }
+
if (xul_param('title')) {
try { document.title = xul_param('title'); } catch(E) {}
try { window.title = xul_param('title'); } catch(E) {}
</popupset>
<vbox flex="1">
- <hbox>
+ <hbox id="browser_toolbar">
<button id="back" command="cmd_back" disabled="true" hidden="true"/>
<button id="reload" command="cmd_reload" disabled="false" hidden="false"/>
<button id="forward" command="cmd_forward" disabled="true" hidden="true"/>
'set_text',
'save_attributes',
'load_attributes',
+ 'find_descendants_by_name'
];
util.widgets.EXPORT_TAGS = { ':all' : util.widgets.EXPORT_OK };
e.setAttribute('properties', prop_class_string);
}
+util.widgets.find_descendants_by_name = function(top_node,name) {
+ top_node = util.widgets.get(top_node);
+ if (!top_node) { return []; }
+ return top_node.getElementsByAttribute('name',name);
+}
+
dump('exiting util/widgets.js\n');
// list of documents for debugging. BROKEN
'doc_list' : [],
- // Windows need unique names. This number helps.
- 'window_name_increment' : function() {
- JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
- if (typeof data.window_name_increment == 'undefined') {
- data.window_name_increment = 1;
- } else {
- data.window_name_increment++;
- }
- data.stash('window_name_increment');
- return data.window_name_increment;
- },
-
// This number gets put into the title bar for Top Level menu interface windows
'appshell_name_increment' : function() {
JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
'open' : function(url,title,features,my_xulG) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var key;
- if (!title) title = 'anon' + this.window_name_increment();
+ if (!title) title = '_blank';
if (!features) features = 'chrome';
this.error.sdump('D_WIN', 'opening ' + url + ', ' + title + ', ' + features + ' from ' + this.win + '\n');
var data;
menu.cmd_browse_hold_pull_list.tab=On Shelf Pull List
menu.cmd_local_admin.tab=Local Administration
menu.cmd_open_vandelay.tab=MARC Import/Export
+menu.cmd_marc_batch_edit.tab=MARC Batch Edit
menu.cmd_open_conify.tab=Server Settings
menu.cmd_retrieve_last_patron.session.error=No patron visited yet this session.
menu.cmd_retrieve_last_record.session.error=No record visited yet this session.
menu.cmd_verify_credentials.tabname=Verify Credentials
menu.close_all_tabs.error=Error closing all tabs
menu.new_tab.tab=Tab
+menu.new_tab.max_tab_dialog=Sorry, we can't create any more tabs in this window.\nWould you like to create a new tab in another window?
main.session_cookie.error=Error setting session cookie: %1$s
menu.set_tab.error=pause for error
menu.reset_network_stats=Reset network activity summary?
printing.prompt_for_external_print_cmd=Enter external print command and parameters (use %receipt.txt% or %receipt.html% as the file containing the print data. Those values will be substituted with the proper path.):
printing.print_strategy_saved=Print strategy (%1$s) for %2$s context saved to file system.
text_editor.prompt_for_external_cmd=Enter external text editor command and parameters (use %letter.txt% as the file containing the text. This value will be substituted with the proper path.):
+menu.tab1.accesskey=1
+menu.tab2.accesskey=2
+menu.tab3.accesskey=3
+menu.tab4.accesskey=4
+menu.tab5.accesskey=5
+menu.tab6.accesskey=6
+menu.tab7.accesskey=7
+menu.tab8.accesskey=8
+menu.tab9.accesskey=9
+menu.tab10.accesskey=0
--- /dev/null
+const nsISupports = Components.interfaces.nsISupports;\r
+const nsICategoryManager = Components.interfaces.nsICategoryManager;\r
+const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;\r
+const nsICommandLine = Components.interfaces.nsICommandLine;\r
+const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler;\r
+const nsIFactory = Components.interfaces.nsIFactory;\r
+const nsIModule = Components.interfaces.nsIModule;\r
+const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;\r
+\r
+\r
+const XUL_STANDALONE = "chrome://open_ils_staff_client/content/circ/offline.xul";\r
+const XUL_MAIN = "chrome://open_ils_staff_client/content/main/main.xul";\r
+const WINDOW_STANDALONE = "eg_offline"\r
+const WINDOW_MAIN = "eg_main"\r
+\r
+const clh_contractID = "@mozilla.org/commandlinehandler/general-startup;1?type=egcli";\r
+const clh_CID = Components.ID("{7e608198-7355-483a-a85a-20322e4ef91a}");\r
+// category names are sorted alphabetically. Typical command-line handlers use a\r
+// category that begins with the letter "m".\r
+const clh_category = "m-egcli";\r
+\r
+/**\r
+ * Utility functions\r
+ */\r
+\r
+/**\r
+ * Opens a chrome window.\r
+ * @param aChromeURISpec a string specifying the URI of the window to open.\r
+ * @param aArgument an argument to pass to the window (may be null)\r
+ */\r
+function findOrOpenWindow(aWindowType, aChromeURISpec, aName, aArgument)\r
+{\r
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].\r
+ getService(Components.interfaces.nsIWindowMediator);\r
+ var targetWindow = wm.getMostRecentWindow(aWindowType);\r
+ if (targetWindow != null) {\r
+ if(typeof targetWindow.new_tabs == 'function' && aArgument != null)\r
+ {\r
+ targetWindow.new_tabs(aArgument);\r
+ }\r
+ else {\r
+ targetwindow.focus;\r
+ }\r
+ }\r
+ else {\r
+ var params = null;\r
+ if (aArgument != null && aArgument.length != 0)\r
+ {\r
+ params = { "openTabs" : aArgument };\r
+ params.wrappedJSObject = params;\r
+ }\r
+ var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].\r
+ getService(Components.interfaces.nsIWindowWatcher);\r
+ ww.openWindow(null, aChromeURISpec, aName,\r
+ "chrome,resizable,dialog=no", params);\r
+ }\r
+}\r
+ \r
+/**\r
+ * The XPCOM component that implements nsICommandLineHandler.\r
+ * It also implements nsIFactory to serve as its own singleton factory.\r
+ */\r
+const myAppHandler = {\r
+ /* nsISupports */\r
+ QueryInterface : function clh_QI(iid)\r
+ {\r
+ if (iid.equals(nsICommandLineHandler) ||\r
+ iid.equals(nsIFactory) ||\r
+ iid.equals(nsISupports))\r
+ return this;\r
+\r
+ throw Components.results.NS_ERROR_NO_INTERFACE;\r
+ },\r
+\r
+ /* nsICommandLineHandler */\r
+\r
+ handle : function clh_handle(cmdLine)\r
+ {\r
+ // Each of these options is used for opening a new tab, either on login or remote send.\r
+ // XULRunner does some sanitize to turn /ilsblah into -ilsblah, for example.\r
+ // In addition to the ones here, -ilslogin, -ilsoffline, and -ilsstandalone\r
+ // are defined below.\r
+\r
+ // With the exception of 'new', 'tab', and 'init', the value is checked for in urls in main.js.\r
+\r
+ // NOTE: The option itself should be all lowercase (we .toLowerCase below)\r
+ var options = {\r
+ '-ilscheckin' : 'XUL_CHECKIN',\r
+ '-ilscheckout' : 'XUL_PATRON_BARCODE_ENTRY',\r
+ '-ilsnew' : 'new', // 'new' is a special keyword for opening a new window\r
+ '-ilstab' : 'tab', // 'tab' is a special keyword for opening a new tab with the default content\r
+ '-ilsnew_default' : 'init', // 'init' is a special keyword for opening a new window with an initial default tab\r
+ };\r
+\r
+ var inParams = new Array();\r
+ var position = 0;\r
+ while (position < cmdLine.length) {\r
+ var arg = cmdLine.getArgument(position).toLowerCase();\r
+ if (options[arg] != undefined) {\r
+ inParams.push(options[arg]);\r
+ cmdLine.removeArguments(position,position);\r
+ continue;\r
+ }\r
+ if (arg == '-ilsurl' && cmdLine.length > position) {\r
+ inParams.push(cmdLine.getArgument(position + 1));\r
+ cmdLine.removeArguments(position, position + 1);\r
+ continue;\r
+ }\r
+ position=position + 1;\r
+ }\r
+\r
+ if (cmdLine.handleFlag("ILSlogin", false) || inParams.length > 0) {\r
+ findOrOpenWindow(WINDOW_MAIN, XUL_MAIN, '_blank', inParams);\r
+ cmdLine.preventDefault = true;\r
+ }\r
+\r
+ if (cmdLine.handleFlag("ILSoffline", false) || cmdLine.handleFlag("ILSstandalone", false)) {\r
+ findOrOpenWindow(WINDOW_STANDALONE, XUL_STANDALONE, 'Offline', null);\r
+ cmdLine.preventDefault = true;\r
+ }\r
+ },\r
+\r
+ // CHANGEME: change the help info as appropriate, but\r
+ // follow the guidelines in nsICommandLineHandler.idl\r
+ // specifically, flag descriptions should start at\r
+ // character 24, and lines should be wrapped at\r
+ // 72 characters with embedded newlines,\r
+ // and finally, the string should end with a newline\r
+ helpInfo : " -ILScheckin Open an Evergreen checkin tab\n" +\r
+ " -ILScheckout Open an Evergreen checkout tab\n" +\r
+ " -ILSnew Open a new Evergreen 'menu' window\n" +\r
+ " -ILSnew_default Open a new Evergreen 'menu' window,\n" +\r
+ " with a 'default' tab\n" +\r
+ " -ILStab Open a 'default' tab alone\n" +\r
+ " -ILSurl <url> Open the specified url in an Evergreen tab\n" +\r
+ " The above six imply -ILSlogin\n" +\r
+ " -ILSlogin Open the Evergreen Login window\n" +\r
+ " -ILSstandalone Open the Evergreen Standalone interface\n" +\r
+ " -ILSoffline Alias for -ILSstandalone\n",\r
+\r
+ /* nsIFactory */\r
+\r
+ createInstance : function clh_CI(outer, iid)\r
+ {\r
+ if (outer != null)\r
+ throw Components.results.NS_ERROR_NO_AGGREGATION;\r
+\r
+ return this.QueryInterface(iid);\r
+ },\r
+\r
+ lockFactory : function clh_lock(lock)\r
+ {\r
+ /* no-op */\r
+ }\r
+};\r
+\r
+/**\r
+ * The XPCOM glue that implements nsIModule\r
+ */\r
+const myAppHandlerModule = {\r
+ /* nsISupports */\r
+ QueryInterface : function mod_QI(iid)\r
+ {\r
+ if (iid.equals(nsIModule) ||\r
+ iid.equals(nsISupports))\r
+ return this;\r
+\r
+ throw Components.results.NS_ERROR_NO_INTERFACE;\r
+ },\r
+\r
+ /* nsIModule */\r
+ getClassObject : function mod_gch(compMgr, cid, iid)\r
+ {\r
+ if (cid.equals(clh_CID))\r
+ return myAppHandler.QueryInterface(iid);\r
+\r
+ throw Components.results.NS_ERROR_NOT_REGISTERED;\r
+ },\r
+\r
+ registerSelf : function mod_regself(compMgr, fileSpec, location, type)\r
+ {\r
+ compMgr.QueryInterface(nsIComponentRegistrar);\r
+\r
+ compMgr.registerFactoryLocation(clh_CID,\r
+ "myAppHandler",\r
+ clh_contractID,\r
+ fileSpec,\r
+ location,\r
+ type);\r
+\r
+ var catMan = Components.classes["@mozilla.org/categorymanager;1"].\r
+ getService(nsICategoryManager);\r
+ catMan.addCategoryEntry("command-line-handler",\r
+ clh_category,\r
+ clh_contractID, true, true);\r
+ },\r
+\r
+ unregisterSelf : function mod_unreg(compMgr, location, type)\r
+ {\r
+ compMgr.QueryInterface(nsIComponentRegistrar);\r
+ compMgr.unregisterFactoryLocation(clh_CID, location);\r
+\r
+ var catMan = Components.classes["@mozilla.org/categorymanager;1"].\r
+ getService(nsICategoryManager);\r
+ catMan.deleteCategoryEntry("command-line-handler", clh_category);\r
+ },\r
+\r
+ canUnload : function (compMgr)\r
+ {\r
+ return true;\r
+ }\r
+};\r
+\r
+/* The NSGetModule function is the magic entry point that XPCOM uses to find what XPCOM objects\r
+ * this component provides\r
+ */\r
+function NSGetModule(comMgr, fileSpec)\r
+{\r
+ return myAppHandlerModule;\r
+}\r
+\r
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="main_test_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="example_template_win"
- onload="try { my_init(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="main_test_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="font_settings_win"
- onload="try { my_init(); } catch(E) { alert(E); }" style="background: white;"
+ onload="try { my_init(); persist_helper(); } catch(E) { alert(E); }" style="background: white;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
function getBuildId() { return location.href.match(/\/xul\/(.+?)\/server\//)[1]; }
function my_init() {
- try {
- dojo.require("dojo.cookie");
- window.xulG.auth = {"session": {"key": dojo.cookie("ses")}};
- } catch(E) { /* XXX ignorable except for booking links */ }
+ ;
}
</script>
<style type='text/css'>
<th width='25%'>&staff.server.admin.index.workstation_configuration;</th>
<th width='25%'>&staff.server.admin.index.library_configuration;</th>
<th width='25%'>&staff.server.admin.index.maintenance_reports;</th>
- <th width='25%'>&staff.server.admin.index.booking;</th>
</tr>
</thead>
<tbody>
</div>
</td><td>
<div style='padding: 8px;'>
+ <a href='javascript:window.xulG.new_tab(window.xulG.url_prefix("/opac/extras/circ/alt_holds_print.html").replace("http","https") + "?do=shelf_expired_holds&chunk_size=25",{"tab_name":"&staff.server.admin.index.expired_holds_shelf;"},{});'>&staff.server.admin.index.expired_holds_shelf;</a>
+ </div>
+ <div style='padding: 8px;'>
<a href='javascript:window.xulG.new_tab("/xul/server/patron/holds.xul",{"tab_name":"&staff.server.admin.index.hold_pull_list;"},{});'>&staff.server.admin.index.hold_pull_list;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
</div>
<div style='padding: 8px;'>
<div style='padding: 8px;'>
<a href='javascript:window.xulG.new_tab("/xul/server/admin/transit_list.xul",{"tab_name":"&staff.server.admin.index.transits;"},{});'>&staff.server.admin.index.transit_list;</a>
</div>
- </td><td>
- <div style='padding: 8px;'>
- <a href='javascript:window.xulG.new_tab("/eg/booking/reservation",{"tab_name":"&staff.server.admin.index.booking.reservation;","browser":false},window.xulG);'>&staff.server.admin.index.booking.reservation;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
- </div>
- <div style='padding: 8px;'>
- <a href='javascript:window.xulG.new_tab("/eg/booking/pull_list",{"tab_name":"&staff.server.admin.index.booking.pull_list;","browser":false},window.xulG);'>&staff.server.admin.index.booking.pull_list;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
- </div>
- <div style='padding: 8px;'>
- <a href='javascript:window.xulG.new_tab("/eg/booking/capture",{"tab_name":"&staff.server.admin.index.booking.capture;","browser":false},window.xulG);'>&staff.server.admin.index.booking.capture;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
- </div>
- <div style='padding: 8px;'>
- <a href='javascript:window.xulG.new_tab("/eg/booking/pickup",{"tab_name":"&staff.server.admin.index.booking.pickup;","browser":false},window.xulG);'>&staff.server.admin.index.booking.pickup;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
- </div>
- <div style='padding: 8px;'>
- <a href='javascript:window.xulG.new_tab("/eg/booking/return",{"tab_name":"&staff.server.admin.index.booking.return;","browser":false},window.xulG);'>&staff.server.admin.index.booking.return;</a> <span style="color: red">&staff.server.admin.index.testing;</span>
- </div>
</td>
</tr>
</tbody>
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="admin_offline_manage_xacts_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<vbox id="admin_offline_manage_xacts_main" flex="1">
- <groupbox flex="1">
+ <groupbox flex="1" id="before_splitter" oils_persist="height">
<caption label="&staff.server.admin.offline.xacts.caption;"/>
<hbox>
<button id="refresh" label="&common.refresh;" accesskey="&staff.server.admin.offline.xacts.refresh.accesskey;"/>
</hbox>
<tree id="session_tree" enableColumnDrag="true" seltype="single" flex="1"/>
</groupbox>
- <splitter><grippy/></splitter>
- <deck flex="1" id="deck">
+ <splitter id="splitter" oils_persist="state" oils_persist_peers="before_splitter deck"><grippy/></splitter>
+ <deck flex="1" id="deck" oils_persist="height">
<label value=" "/>
<groupbox flex="1">
<caption id="status_caption" label="&staff.server.admin.offline.xacts.status.label;"/>
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="admin_transit_list_win" active="true"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<script type="text/javascript" src="work_log.js"/>
<vbox flex="1">
- <vbox flex="1">
+ <vbox flex="1" id="before_splitter" oils_persist="height">
<hbox>
<textbox id="desire_number_of_work_log_entries" type="number" oils_persist="value" />
<label value="&staff.admin.work_log.list1.header;" class="header1"/>
</hbox>
<tree id="work_action_log" flex="1" enableColumnDrag="true" context="work_log_actions"/>
</vbox>
- <splitter><grippy/></splitter>
- <vbox flex="1">
+ <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy/></splitter>
+ <vbox flex="1" id="after_splitter" oils_persist="height">
<hbox>
<textbox id="desire_number_of_patron_log_entries" type="number" oils_persist="value" />
<label value="&staff.admin.work_log.list2.header;" class="header1"/>
<?xul-overlay href="/xul/server/cat/bib_brief_overlay.xul"?>
<window id="cat_bib_brief_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE overlay PUBLIC "" ""[
+ <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+<overlay id="bib_brief_overlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="text/javascript" src="/xul/server/cat/bib_brief_overlay.js"/>
+
+ <grid id="bib_brief_grid" flex="0">
+ <columns>
+ <column />
+ <column />
+ </columns>
+ <rows id="bib_brief_grid_rows">
+ <row id="bib_brief_grid_row1" position="1">
+ <label value="&staff.cat.bib_brief.title.label;" accesskey="&staff.cat.bib_brief.title.accesskey;" control="title" class="emphasis"/>
+ <textbox id="title" name="title" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label value="&staff.cat.bib_brief.author.label;" accesskey="&staff.cat.bib_brief.author.accesskey;" control="author" class="emphasis"/>
+ <textbox id="author" name="author" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row position="2">
+ <label value="&staff.cat.bib_brief.edition.label;" accesskey="&staff.cat.bib_brief.edition.accesskey;" control="edition" class="emphasis"/>
+ <textbox id="edition" name="edition" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label value="&staff.cat.bib_brief.pub_date.label;" accesskey="&staff.cat.bib_brief.pub_date.accesskey;" control="pubdate" class="emphasis"/>
+ <textbox id="pubdate" name="pubdate" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label id="bib_call_number_label" value="&staff.cat.bib_brief.call_number.label;" accesskey="&staff.cat.bib_brief.call_number.accesskey;" control="bib_call_number" class="emphasis"/>
+ <textbox id="bib_call_number" name="bib_call_number" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row id="bib_brief_grid_row3" position="3">
+ <label value="&staff.cat.bib_brief.title_control_number.label;" accesskey="&staff.cat.bib_brief.title_control_number.accesskey;" control="tcn" class="emphasis"/>
+ <textbox id="tcn" name="tcn" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label value="&staff.cat.bib_brief.biblio_record_entry_id.label;" accesskey="&staff.cat.bib_brief.biblio_record_entry_id.accesskey;" control="mvr_doc_id" class="emphasis"/>
+ <textbox id="mvr_doc_id" name="mvr_doc_id" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label value="&staff.cat.bib_brief.biblio_record_entry_owner.label;" accesskey="&staff.cat.bib_brief.biblio_record_entry_owner.accesskey;" control="owner" class="emphasis"/>
+ <textbox id="owner" name="owner" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label value="&staff.cat.bib_brief.created_by.label;" accesskey="&staff.cat.bib_brief.created_by.accesskey;" control="creator" class="emphasis"/>
+ <textbox id="creator" name="creator" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label value="&staff.cat.bib_brief.last_edited_by.label;" accesskey="&staff.cat.bib_brief.last_edited_by.accesskey;" control="editor" class="emphasis"/>
+ <textbox id="editor" name="editor" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ <row>
+ <label value="&staff.cat.bib_brief.last_edited_on.label;" accesskey="&staff.cat.bib_brief.last_edited_on.accesskey;" control="edit_date" class="emphasis"/>
+ <textbox id="edit_date" name="edit_date" readonly="true" context="clipboard" class="plain" onfocus="this.select()"/>
+ </row>
+ </rows>
+ </grid>
+</overlay>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Application: Evergreen Staff Client -->
+<!-- Screen: Brief Bib Display -->
+<!--
+vim: noet:sw=4:ts=4:
+-->
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- STYLESHEETS -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- LOCALIZATION -->
+<!DOCTYPE window PUBLIC "" ""[
+ <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- OVERLAYS -->
+<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
+<?xul-overlay href="/xul/server/cat/bib_brief_overlay_vertical.xul"?>
+
+<window id="cat_bib_brief_win"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+ <!-- BEHAVIOR -->
+ <script type="text/javascript">
+ var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true; var g = {};
+ </script>
+ <scripts id="openils_util_scripts"/>
+
+ <script type="text/javascript" src="/xul/server/main/JSAN.js"/>
+ <script type="text/javascript" src="/xul/server/cat/bib_brief.js"/>
+
+ <messagecatalog id="catStrings" src="/xul/server/locale/<!--#echo var='locale'-->/cat.properties"/>
+ <messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties"/>
+
+ <groupbox id="groupbox" flex="1">
+ <caption id="caption"><label value="&staff.cat.bib_brief.record_summary;"/>(<label value="&staff.cat.bib_brief.view_marc;" class="click_link" onclick="view_marc();"/>)</caption>
+ <grid id="bib_brief_grid" />
+ </groupbox>
+
+</window>
+
--- /dev/null
+var error;
+var network;
+var record_ids;
+var lead_record;
+
+function my_init() {
+ try {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ if (typeof JSAN == 'undefined') { throw( "The JSAN library object is missing."); }
+ JSAN.errorLevel = "die"; // none, warn, or die
+ JSAN.addRepository('/xul/server/');
+ JSAN.use('util.error'); error = new util.error();
+ error.sdump('D_TRACE','my_init() for bibs_abreast.xul');
+ JSAN.use('util.functional');
+ JSAN.use('util.widgets');
+ JSAN.use('cat.util');
+ JSAN.use('util.network');
+
+ network = new util.network();
+
+ record_ids = xul_param('record_ids') || [];
+ record_ids = util.functional.unique_list_values( record_ids );
+
+ // Merge UI
+ if (xul_param('merge')) {
+ var x = document.getElementById('merge_bar');
+ x.hidden = false;
+ var y = document.getElementById('merge_button');
+ y.addEventListener('command', merge_records, false);
+ var z = document.getElementById('cancel_button');
+ z.addEventListener('command', function() {
+ x.hidden = true;
+ y.disabled = true;
+ var merge_bars = util.widgets.find_descendants_by_name(document,'merge_bar');
+ for (var i = 0; i < merge_bars.length; i++) { merge_bars[i].hidden = true; }
+ }, false);
+ }
+
+ // Display the records
+ for (var i = 0; i < record_ids.length; i++) {
+ render_bib(record_ids[i]);
+ }
+
+ /*if (typeof window.xulG == 'object' && typeof window.xulG.set_tab_name == 'function') {
+ try { window.xulG.set_tab_name('Test'); } catch(E) { alert(E); }
+ }*/
+
+ } catch(E) {
+ try { error.standard_unexpected_error_alert('main/test.xul',E); } catch(F) { alert(E); }
+ }
+}
+
+function render_bib(record_id) {
+ var main = document.getElementById('main');
+ var template = main.firstChild;
+ var new_node = template.cloneNode(true);
+ main.appendChild(new_node);
+ new_node.hidden = false;
+
+ var splitter_template = template.nextSibling;
+ var splitter = splitter_template.cloneNode(true);
+ main.appendChild(splitter);
+ splitter.hidden = false;
+
+ render_bib_brief(new_node,record_id);
+
+ var xul_deck = util.widgets.find_descendants_by_name(new_node,'bib_deck')[0];
+ var deck = new util.deck(xul_deck);
+
+ // merge UI
+ if (xul_param('merge')) {
+ var merge_bar = util.widgets.find_descendants_by_name(new_node,'merge_bar')[0];
+ merge_bar.hidden = false;
+ var lead_button = util.widgets.find_descendants_by_name(new_node,'lead_button')[0];
+ lead_button.addEventListener('click', function() {
+ lead_record = record_id;
+ dump('record_id = ' + record_id + '\n');
+ document.getElementById('merge_button').disabled = false;
+ }, false);
+ }
+
+ // remove_me button
+ var remove_me = util.widgets.find_descendants_by_name(new_node,'remove_me')[0];
+ remove_me.addEventListener('command', function() {
+ if (lead_record == record_id) {
+ lead_record = undefined;
+ document.getElementById('merge_button').disabled = true;
+ }
+ record_ids = util.functional.filter_list( record_ids, function(o) { return o != record_id; } );
+ main.removeChild(new_node);
+ main.removeChild(splitter);
+ if (main.childNodes.length == 4) {
+ document.getElementById('merge_bar').hidden = true;
+ document.getElementById('merge_button').disabled = true;
+ var merge_bars = util.widgets.find_descendants_by_name(document,'merge_bar');
+ for (var i = 0; i < merge_bars.length; i++) { merge_bars[i].hidden = true; }
+ }
+ if (main.childNodes.length == 2) { xulG.close_tab(); }
+ }, false);
+
+ // radio buttons
+ var view_bib = util.widgets.find_descendants_by_name(new_node,'view_bib')[0];
+ var edit_bib = util.widgets.find_descendants_by_name(new_node,'edit_bib')[0];
+ var holdings = util.widgets.find_descendants_by_name(new_node,'holdings')[0];
+
+ view_bib.addEventListener('command', function() {
+ set_view_pane(deck,record_id);
+ }, false);
+
+ edit_bib.addEventListener('command', function() {
+ set_edit_pane(deck,record_id);
+ }, false);
+
+ holdings.addEventListener('command', function() {
+ set_item_pane(deck,record_id);
+ }, false);
+
+ set_view_pane(deck,record_id);
+
+}
+
+function render_bib_brief(new_node,record_id) {
+ // iframe
+ var bib_brief = util.widgets.find_descendants_by_name(new_node,'bib_brief')[0];
+ bib_brief.setAttribute('src', urls.XUL_BIB_BRIEF_VERTICAL);
+ get_contentWindow(bib_brief).xulG = { 'docid' : record_id };
+}
+
+function set_view_pane(deck,record_id) {
+ deck.set_iframe( urls.XUL_MARC_VIEW, {}, { 'docid' : record_id } );
+}
+
+function set_item_pane(deck,record_id) {
+ var my_xulG = { 'docid' : record_id }; for (var i in xulG) { my_xulG[i] = xulG[i]; }
+ deck.set_iframe( urls.XUL_COPY_VOLUME_BROWSE, {}, my_xulG );
+}
+
+function set_edit_pane(deck,record_id) {
+ var my_xulG = {
+ 'record' : { 'url' : '/opac/extras/supercat/retrieve/marcxml/record/' + record_id, "id": record_id, "rtype": "bre" },
+ 'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
+ try {
+ return cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
+ } catch(E) {
+ alert('Error in bibs_abreast.js, set_edit_pane, fast_item_add: ' + E);
+ }
+ },
+ 'save' : {
+ 'label' : document.getElementById('offlineStrings').getString('cat.save_record'),
+ 'func' : function (new_marcxml) {
+ try {
+ var r = network.simple_request('MARC_XML_RECORD_UPDATE', [ ses(), record_id, new_marcxml ]);
+ if (typeof r.ilsevent != 'undefined') {
+ throw(r);
+ } else {
+ return {
+ 'id' : r.id(),
+ 'oncomplete' : function() {}
+ };
+ }
+ } catch(E) {
+ alert('Error in bibs_abreast.js, set_edit_pane, save: ' + E);
+ }
+ }
+ }
+ };
+ for (var i in xulG) { my_xulG[i] = xulG[i]; }
+ deck.set_iframe( urls.XUL_MARC_EDIT, {}, my_xulG );
+}
+
+function merge_records() {
+ try {
+ var robj = network.simple_request('MERGE_RECORDS',
+ [
+ ses(),
+ lead_record,
+ util.functional.filter_list( record_ids,
+ function(o) {
+ return o != lead_record;
+ }
+ )
+ ]
+ );
+ if (typeof robj.ilsevent != 'undefined') {
+ switch(robj.ilsevent) {
+ case 5000 /* PERM_FAILURE */: break;
+ default: throw(robj);
+ }
+ }
+ if (typeof xulG.on_merge == 'function') {
+ xulG.on_merge(robj);
+ }
+ var opac_url = xulG.url_prefix( urls.opac_rdetail ) + '?r=' + lead_record;
+ var content_params = {
+ 'session' : ses(),
+ 'authtime' : ses('authtime'),
+ 'opac_url' : opac_url,
+ };
+ xulG.set_tab(
+ xulG.url_prefix(urls.XUL_OPAC_WRAPPER),
+ {'tab_name':'Retrieving title...'},
+ content_params
+ );
+ } catch(E) {
+ alert('Error in bibs_abreast.js, merge_records(): ' + E);
+ }
+}
--- /dev/null
+<?xml version="1.0"?>
+<!-- Application: Evergreen Staff Client -->
+<!-- Screen: Example Template for remote xul -->
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- STYLESHEETS -->
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- LOCALIZATION -->
+<!DOCTYPE window PUBLIC "" ""[
+ <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+
+<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+<!-- OVERLAYS -->
+<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
+
+<window id="main_bibs_abreast"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
+ <!-- BEHAVIOR -->
+ <script type="text/javascript">
+ var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true;
+ </script>
+ <scripts id="openils_util_scripts"/>
+
+ <script type="text/javascript" src="/xul/server/main/JSAN.js"/>
+ <script type="text/javascript" src="bibs_abreast.js"/>
+
+ <hbox id="merge_bar" hidden="true">
+ <description>&staff.cat.record_buckets.merge_records.merge_lead;</description>
+ <button id="merge_button" disabled="true"
+ label="&staff.cat.record_buckets.merge_records.button.label;"
+ accesskey="&staff.cat.record_buckets.merge_records.button.accesskey;"
+ />
+ <button id="cancel_button"
+ label="&staff.cat.record_buckets.merge_records.cancel_button.label;"
+ accesskey="&staff.cat.record_buckets.merge_records.cancel_button.accesskey;"
+ />
+ </hbox>
+ <hbox id="main" class="my_overflow" flex="1">
+ <vbox name="template" hidden="true" flex="1">
+ <hbox name="merge_bar" hidden="true">
+ <input name="lead_button" type="radio" xmlns="http://www.w3.org/1999/xhtml"></input>
+ &staff.cat.record_buckets.merge_records.lead;
+ </hbox>
+ <iframe name="bib_brief" flex="1" />
+ <hbox>
+ <radiogroup flex="0" orient="horizontal">
+ <radio name="view_bib" label="View Bib" />
+ <radio name="edit_bib" label="Edit Bib" />
+ <radio name="holdings" label="Holdings" />
+ </radiogroup>
+ <spacer flex="1"/>
+ <button name="remove_me"
+ image="/xul/server/skin/media/images/icon_delete.gif"
+ label="&staff.cat.record_buckets.merge_records.remove_from_consideration;" />
+ </hbox>
+ <deck name="bib_deck" flex="3" />
+ </vbox>
+ <splitter name="template2" hidden="true"><grippy /></splitter>
+ </hbox>
+
+</window>
+
var obj = this;
try {
var org = obj.data.hash.aou[ org_id ];
- if (obj.data.hash.aout[ org.ou_type() ].depth() == 0 && ! get_bool( obj.data.hash.aout[ org.ou_type() ].can_have_vols() ) ) return;
obj.funcs.push( function() {
document.getElementById('cmd_refresh_list').setAttribute('disabled','true');
document.getElementById('cmd_show_libs_with_copies').setAttribute('disabled','true');
}
if (document.getElementById('show_acns').checked) {
- if (! ( obj.data.hash.aout[ org.ou_type() ].depth() == 0 && ! get_bool( obj.data.hash.aout[ org.ou_type() ].can_have_vols() ) )) {
- node.setAttribute('open','true');
- obj.funcs.push( function() { obj.on_select_org( org.id() ); } );
- }
+ node.setAttribute('open','true');
+ obj.funcs.push( function() { obj.on_select_org( org.id() ); } );
}
} catch(E) {
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="cat_copy_browser" active="true"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
</box>
<vbox id="cmvb1" flex="1">
- <groupbox flex="1">
+ <groupbox flex="1" id="before_splitter" oils_persist="height">
<caption label="&staff.cat.copy_buckets_overlay.pending_copies;" />
<hbox id="pending_buckets_top_ui" />
<tree id="pending_copies_list" flex="1" enableColumnDrag="true"/>
<hbox id="pending_buckets_bottom_ui" />
</groupbox>
- <splitter><grippy /></splitter>
- <groupbox flex="2">
+ <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy /></splitter>
+ <groupbox flex="2" id="after_splitter" oils_persist="height">
<caption label="&staff.cat.copy_buckets_overlay.bucket_view;" />
<hbox id="copy_buckets_top_ui" />
<hbox id="info_box"/>
</hbox>
<hbox flex="1" style="overflow: scroll">
- <vbox flex="1">
+ <vbox flex="1" id="before_splitter1" oils_persist="width">
<label value="&staff.cat.copy_editor.identification.label;" style="font-weight: bold; font-size: large"/>
<vbox id="left_pane" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter1" oils_persist="width">
<button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.location.label;" accesskey="&staff.cat.copy_editor.identification.location.accesskey;" oncommand="document.getElementById('right_pane').firstChild.firstChild.focus();"/>
<vbox id="right_pane" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_splitter1 after_splitter2"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter2" oils_persist="width">
<button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.circulation.label;" accesskey="&staff.cat.copy_editor.identification.circulation.accesskey;" oncommand="document.getElementById('right_pane2').firstChild.firstChild.focus();"/>
<vbox id="right_pane2" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter3" oils_persist="state hidden" oils_persist_peers="after_splitter2 after_splitter3"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter3" oils_persist="width">
<button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.miscellaneous.label;" accesskey="&staff.cat.copy_editor.identification.miscellaneous.accesskey;" oncommand="document.getElementById('right_pane3').firstChild.firstChild.focus();"/>
<vbox id="right_pane3" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter4" oils_persist="state hidden" oils_persist_peers="after_splitter3 after_splitter4"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter4" oils_persist="width">
<button style="font-weight: bold; font-size: normal" label="&staff.cat.copy_editor.identification.statistics.label;" accesskey="&staff.cat.copy_editor.identification.statistics.accesskey;" oncommand="document.getElementById('right_pane4').firstChild.firstChild.focus();"/>
<menu label="&staff.cat.copy_editor.stat_cat_lib_filter_menu.label;" id="stat_cat_lib_filter_menu">
<menupopup />
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="copy_notes_win" width="700" height="550"
+ oils_persist="height width sizemode"
title="&staff.cat.copy_editor.copy_notes.label;"
- onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="example_template_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="cat_marc_view_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
}
}
-function swap_editors () {
+function set_flat_editor (useFlatText) {
dojo.require('MARC.Record');
var xe = $('xul-editor');
var te = $('text-editor');
- te.hidden = te.hidden ? false : true;
- xe.hidden = xe.hidden ? false : true;
+ if (useFlatText) {
+ if (xe.hidden) { return; }
+ te.hidden = false;
+ xe.hidden = true;
+ } else {
+ if (te.hidden) { return; }
+ te.hidden = true;
+ xe.hidden = false;
+ }
if (te.hidden) {
// get the marcxml from the text box
document.getElementById('save-button').setAttribute('label', window.xulG.save.label);
document.getElementById('save-button').setAttribute('oncommand',
- 'if ($("xul-editor").hidden) swap_editors(); ' +
+ 'if ($("xul-editor").hidden) set_flat_editor(false); ' +
'mangle_005(); ' +
'var xml_string = xml_escape_unicode( xml_record.toXMLString() ); ' +
'save_attempt( xml_string ); ' +
;
// would be good to carve this out into a separate function
- dojo.xhrGet({"url":url, "handleAs":"xml", "load": function(records) {
+ dojo.xhrGet({"url":url, "sync": true, "handleAs":"xml", "load": function(records) {
var create_menu = createMenu({ label: $('catStrings').getString('staff.cat.marcedit.create_authority.label')});
var cm_popup = create_menu.appendChild(
<caption label="&staff.cat.marcedit.options.label;"/>
<hbox flex="1">
<checkbox oils_persist="checked" accesskey="&staff.cat.marcedit.stackSubfields.accesskey;" label="&staff.cat.marcedit.stackSubfields.label;" oncommand="stackSubfields(this);" checked="false" id="stackSubfields"/>
+ <checkbox oils_persist="checked" accesskey="&staff.cat.marcedit.flatTextEditor.accesskey;" label="&staff.cat.marcedit.flatTextEditor.label;" oncommand="set_flat_editor(this.checked);" checked="false" id="swapEditor_checkbox"/>
<checkbox oils_persist="checked" accesskey="&staff.cat.marcedit.fastItemAdd.accesskey;" label="&staff.cat.marcedit.fastItemAdd.label;" oncommand="fastItemAdd_toggle(this);" checked="false" id="fastItemAdd_checkbox"/>
<hbox id="fastItemAdd_textboxes">
<label control="fastItemAdd_callnumber" accesskey="&staff.cat.marcedit.fastItemAdd_callnumber.accesskey;" value="&staff.cat.marcedit.fastItemAdd_callnumber.label;" />
</hbox>
<button label="&staff.cat.marcedit.validate.label;" accesskey="&staff.cat.marcedit.validate.accesskey;" oncommand="validateAuthority(this);"/>
<button id="save-button" accesskey="&staff.cat.marcedit.save-button.accesskey;"/>
- <button label="&staff.cat.marcedit.swapEditor.label;" oncommand="swap_editors()"/>
<button label="&staff.cat.marcedit.help.label;" accesskey="&staff.cat.marcedit.help.accesskey;"
oncommand="alert(
$('catStrings').getString('staff.cat.marcedit.help.add_row') + '\n' +
obj.controller.view.cmd_delete_records.setAttribute('disabled','true');
obj.controller.view.cmd_sel_opac.setAttribute('disabled','true');
obj.controller.view.cmd_transfer_title_holds.setAttribute('disabled','true');
+ obj.controller.view.cmd_marc_batch_edit.setAttribute('disabled','true');
obj.controller.view.record_buckets_list_actions.disabled = true;
var bucket = obj.network.simple_request(
'BUCKET_FLESH',
obj.controller.view.cmd_delete_records.setAttribute('disabled','false');
obj.controller.view.cmd_sel_opac.setAttribute('disabled','false');
obj.controller.view.cmd_transfer_title_holds.setAttribute('disabled','false');
+ obj.controller.view.cmd_marc_batch_edit.setAttribute('disabled','false');
obj.controller.view.record_buckets_list_actions.disabled = false;
var x = document.getElementById('info_box');
obj.controller.view.cmd_delete_records.setAttribute('disabled','true');
obj.controller.view.cmd_sel_opac.setAttribute('disabled','true');
obj.controller.view.cmd_transfer_title_holds.setAttribute('disabled','true');
+ obj.controller.view.cmd_marc_batch_edit.setAttribute('disabled','true');
obj.controller.view.record_buckets_list_actions.disabled = true;
obj.controller.render('record_buckets_menulist_placeholder');
setTimeout(
}
);
- netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
- var top_xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" >';
- top_xml += '<description>' + $("catStrings").getString('staff.cat.record_buckets.merge_records.merge_lead') + '</description>';
- top_xml += '<hbox>';
- top_xml += '<button id="lead" disabled="true" label="'
- + $("catStrings").getString('staff.cat.record_buckets.merge_records.button.label') + '" name="fancy_submit"/>';
- top_xml += '<button label="' + $("catStrings").getString('staff.cat.record_buckets.merge_records.cancel_button.label') +'" accesskey="'
- + $("catStrings").getString('staff.cat.record_buckets.merge_records.cancel_button.accesskey') +'" name="fancy_cancel"/></hbox></vbox>';
-
- var xml = '<form xmlns="http://www.w3.org/1999/xhtml">';
- xml += '<table><tr valign="top">';
- for (var i = 0; i < record_ids.length; i++) {
- xml += '<td><input value="' + $("catStrings").getString('staff.cat.record_buckets.merge_records.lead')
- xml += '" id="record_' + record_ids[i] + '" type="radio" name="lead"';
- xml += ' onclick="' + "try { var x = $('lead'); x.setAttribute('value',";
- xml += record_ids[i] + '); x.disabled = false; } catch(E) { alert(E); }">';
- xml += '</input>' + $("catStrings").getFormattedString('staff.cat.record_buckets.merge_records.lead_record_number',[record_ids[i]]) + '</td>';
- }
- xml += '</tr><tr valign="top">';
- for (var i = 0; i < record_ids.length; i++) {
- xml += '<td nowrap="nowrap"><iframe src="' + urls.XUL_BIB_BRIEF;
- xml += '?docid=' + record_ids[i] + '"/></td>';
- }
- xml += '</tr><tr valign="top">';
- for (var i = 0; i < record_ids.length; i++) {
- xml += '<td nowrap="nowrap"><iframe style="min-height: 1000px; min-width: 300px;" flex="1" src="' + urls.XUL_MARC_VIEW + '?docid=' + record_ids[i] + ' "/></td>';
- }
- xml += '</tr></table></form>';
- //obj.data.temp_merge_top = top_xml; obj.data.stash('temp_merge_top');
- //obj.data.temp_merge_mid = xml; obj.data.stash('temp_merge_mid');
- JSAN.use('util.window'); var win = new util.window();
- var fancy_prompt_data = win.open(
- urls.XUL_FANCY_PROMPT,
- //+ '?xml_in_stash=temp_merge_mid'
- //+ '&top_xml_in_stash=temp_merge_top'
- //+ '&title=' + window.escape('Record Merging'),
- 'fancy_prompt', 'chrome,resizable,modal,width=700,height=500',
- {
- 'top_xml' : top_xml, 'xml' : xml, 'title' : $("catStrings").getString('staff.cat.record_buckets.merge_records.fancy_prompt_title')
+ xulG.new_tab(
+ '/xul/server/cat/bibs_abreast.xul',{
+ 'tab_name' : $("catStrings").getString('staff.cat.record_buckets.merge_records.fancy_prompt_title')
+ },{
+ 'merge' : true,
+ 'on_merge' : function() {
+ obj.render_pending_records(); // FIXME -- need a generic refresh for lists
+ setTimeout(
+ function() {
+ JSAN.use('util.widgets');
+ util.widgets.dispatch('change_bucket',obj.controller.view.bucket_menulist);
+ }, 0
+ );
+ },
+ 'record_ids':record_ids
}
);
- //obj.data.stash_retrieve();
- if (typeof fancy_prompt_data.fancy_status == 'undefined' || fancy_prompt_data.fancy_status == 'incomplete') {
- alert($("catStrings").getString('staff.cat.record_buckets.merge_records.fancy_prompt.alert'));
- return;
- }
- var robj = obj.network.simple_request('MERGE_RECORDS',
- [
- ses(),
- fancy_prompt_data.lead,
- util.functional.filter_list( record_ids,
- function(o) {
- return o != fancy_prompt_data.lead;
- }
- )
- ]
- );
- if (typeof robj.ilsevent != 'undefined') {
- throw(robj);
- }
-
- obj.render_pending_records(); // FIXME -- need a generic refresh for lists
- setTimeout(
- function() {
- JSAN.use('util.widgets');
- util.widgets.dispatch('change_bucket',obj.controller.view.bucket_menulist);
- }, 0
- );
} catch(E) {
obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.record_buckets.merge_records.catch.std_unex_error'),E);
}
}
}
],
+ 'cmd_marc_batch_edit' : [
+ ['command'],
+ function() {
+ try {
+ var bucket_id = obj.controller.view.bucket_menulist.value;
+ if (!bucket_id) return;
+ obj.list2.select_all();
+ xulG.new_tab(
+ urls.MARC_BATCH_EDIT + '?containerid='+bucket_id+'&recordSource=b',
+ {
+ 'tab_name' : $('offlineStrings').getString('menu.cmd_marc_batch_edit.tab')
+ },
+ {}
+ );
+ } catch(E) {
+ alert('Error in record_buckets.js, cmd_marc_batch_edit: ' + E);
+ }
+ }
+ ],
'cmd_transfer_title_holds' : [
['command'],
function() {
<command id="cmd_add_sel_pending_to_record_bucket" />
<command id="cmd_merge_records" disabled="true" />
+ <command id="cmd_marc_batch_edit"
+ label="&staff.cat.record_buckets_overlay.marc_batch_edit.label;"
+ accesskey="&staff.cat.record_buckets_overlay.marc_batch_edit.accesskey;"
+ disabled="true" />
<command id="cmd_transfer_title_holds"
label="&staff.cat.record_buckets_overlay.transfer_title_holds.label;"
accesskey="&staff.cat.record_buckets_overlay.transfer_title_holds.accesskey;"
<button command="cmd_transfer_title_holds" />
<button command="cmd_delete_records" label="&staff.cat.record_buckets_overlay.del_records.label;"/>
<button command="cmd_merge_records" label="&staff.cat.record_buckets_overlay.merge_records.label;"/>
+ <button command="cmd_marc_batch_edit" />
<button id="record_buckets_export_records" label="&staff.cat.record_buckets_overlay.export_records.label;" type="menu" allowevents="true" disabled="true">
<menupopup id="record_buckets_export_record_types" allowevents="true">
<menuitem command="cmd_export_records_usmarc" label="&staff.cat.record_buckets_overlay.menuitem.export_usmarc.label;"/>
w.xulG = {
'url' : 'about:blank',
'show_print_button' : 1,
+ 'printer_context' : 'label',
'alternate_print' : 1,
'no_xulG' : 1,
'title' : $("catStrings").getString('staff.cat.spine_labels.preview.title'),
'url' : 'data:text/html;charset=utf-8,'+window.escape(html),
'html_source' : html,
'show_print_button' : 1,
+ 'printer_context' : 'label',
'no_xulG' : 1
}
);
<vbox id="spine_labels_main" flex="1" class="my_overflow">
<hbox flex="1" class="my_overflow">
- <vbox>
+ <vbox id="before_splitter" oils_persist="width">
<hbox>
<button label="&staff.cat.spine_labels.re-generate.label;"
accesskey="&staff.cat.spine_labels.re-generate.accesskey;" oncommand="generate()"/>
</rows></grid>
<button label="&staff.cat.spine_labels.available_macros.label;" oncommand="show_macros()"/>
</vbox>
- <splitter><grippy/></splitter>
- <vbox id="panel" flex="1" class="my_overflow"/>
+ <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter panel"><grippy/></splitter>
+ <vbox id="panel" flex="1" class="my_overflow" oils_persist="width"/>
</hbox>
</vbox>
data.stash('cb_temp_copy_ids');
win.open(
xulG.url_prefix(urls.XUL_COPY_BUCKETS_QUICK),
- 'sel_bucket_win' + win.window_name_increment(),
+ '_blank',
'chrome,resizable,center'
);
}
JSAN.use('util.window'); var win = new util.window();
win.open(
xulG.url_prefix(urls.XUL_RECORD_BUCKETS_QUICK),
- 'sel_bucket_win' + win.window_name_increment(),
+ '_blank',
'chrome,resizable,modal,center',
{
record_ids: record_ids
if (r == 0) {
var count = 0;
+ JSAN.use('cat.util');
for (var i = 0; i < copies.length; i++) {
try {
var robj = network.simple_request('MARK_ITEM_MISSING_PIECES',[ses(),copies[i].id()]);
print.simple( robj.payload.slip.template_output().data() );
}
// Item Note
- win.open(
- urls.XUL_COPY_NOTES,
- $("catStrings").getString("staff.cat.copy_editor.copy_notes"),
- 'chrome,resizable,modal',
- { 'copy_id' : copies[i].id() }
- );
+ cat.util.spawn_copy_editor( { 'copy_ids' : [ copies[i].id() ], 'edit' : 1 } );
// Patron Message
var my_xulG = win.open(
urls.XUL_NEW_STANDING_PENALTY,
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="example_template_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="cat_volume_copy_creator_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
- width="800" height="580"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+ width="800" height="580" oils_persist="height width sizemode"
title="&staff.cat.volume_copy_creator.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
document.getElementById('sel_clip').setAttribute('disabled', sel.length < 1);
var list = util.functional.map_list(
sel,
- function(o) { return o.getAttribute('retrieve_id'); }
+ function(o) {
+ if ( $('jacket_image') ) {
+ // A side-effect in this map function, mu hahaha
+ if (o.getAttribute('isbn')) {
+ $('jacket_image').setAttribute('src',urls.ac_jacket_large+o.getAttribute('isbn'));
+ $('jacket_image').setAttribute('tooltiptext',urls.ac_jacket_large+o.getAttribute('isbn'));
+ } else {
+ $('jacket_image').setAttribute('src','');
+ $('jacket_image').setAttribute('tooltiptext','');
+ }
+ }
+ return o.getAttribute('retrieve_id');
+ }
);
obj.error.sdump('D_TRACE','cat/z3950: selection list = ' + js2JSON(list) );
obj.controller.view.marc_import.disabled = false;
}
}
);
+ n.my_node.setAttribute('isbn', function(a){return a;}(obj.result_set[ obj.number_of_result_sets ].records[j].mvr).isbn());
if (!f) { n.my_node.parentNode.focus(); f = n; }
}
} else {
'spawn_marc_editor' : function(my_marcxml,biblio_source) {
var obj = this;
- xulG.new_tab(
- xulG.url_prefix(urls.XUL_MARC_EDIT),
- { 'tab_name' : 'MARC Editor' },
- {
- 'record' : { 'marc' : my_marcxml, "rtype": "bre" },
- 'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
- try {
- JSAN.use('cat.util'); return cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
- } catch(E) {
- alert(E);
- }
- },
- 'save' : {
- 'label' : 'Import Record',
- 'func' : function (new_marcxml) {
- try {
- var r = obj.network.simple_request('MARC_XML_RECORD_IMPORT', [ ses(), new_marcxml, biblio_source ]);
- if (typeof r.ilsevent != 'undefined') {
- switch(Number(r.ilsevent)) {
- case 1704 /* TCN_EXISTS */ :
- var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.same_tcn', [r.payload.tcn]);
- var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.title');
- var btn1 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn1_overlay');
- var btn2 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.btn2_import', [r.payload.new_tcn]);
- if (btn2) {
- obj.data.init({'via':'stash'});
- var robj = obj.network.simple_request(
- 'PERM_CHECK',[
- ses(),
- obj.data.list.au[0].id(),
- obj.data.list.au[0].ws_ou(),
- [ 'ALLOW_ALT_TCN' ]
- ]
- );
- if (typeof robj.ilsevent != 'undefined') {
- obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.permission_error'),E);
- }
- if (robj.length != 0) btn2 = null;
- }
- var btn3 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn3_cancel_import');
- var p = obj.error.yns_alert(msg,title,btn1,btn2,btn3,$("catStrings").getString('staff.cat.z3950.spawn_marc_editor.confirm_action'));
- obj.error.sdump('D_ERROR','option ' + p + 'chosen');
- switch(p) {
- case 0:
- var r3 = obj.network.simple_request('MARC_XML_RECORD_UPDATE', [ ses(), r.payload.dup_record, new_marcxml, biblio_source ]);
- if (typeof r3.ilsevent != 'undefined') {
- throw(r3);
- } else {
- alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_overlay'));
- return {
- 'id' : r3.id(),
- 'on_complete' : function() {
- try {
- obj.replace_tab_with_opac(r3.id());
- } catch(E) {
- alert(E);
- }
- }
- };
+
+ function save_marc (new_marcxml) {
+ try {
+ var r = obj.network.simple_request('MARC_XML_RECORD_IMPORT', [ ses(), new_marcxml, biblio_source ]);
+ if (typeof r.ilsevent != 'undefined') {
+ switch(Number(r.ilsevent)) {
+ case 1704 /* TCN_EXISTS */ :
+ var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.same_tcn', [r.payload.tcn]);
+ var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.title');
+ var btn1 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn1_overlay');
+ var btn2 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor.btn2_import', [r.payload.new_tcn]);
+ if (btn2) {
+ obj.data.init({'via':'stash'});
+ var robj = obj.network.simple_request(
+ 'PERM_CHECK',[
+ ses(),
+ obj.data.list.au[0].id(),
+ obj.data.list.au[0].ws_ou(),
+ [ 'ALLOW_ALT_TCN' ]
+ ]
+ );
+ if (typeof robj.ilsevent != 'undefined') {
+ obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.permission_error'),E);
+ }
+ if (robj.length != 0) btn2 = null;
+ }
+ var btn3 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.btn3_cancel_import');
+ var p = obj.error.yns_alert(msg,title,btn1,btn2,btn3,$("catStrings").getString('staff.cat.z3950.spawn_marc_editor.confirm_action'));
+ obj.error.sdump('D_ERROR','option ' + p + 'chosen');
+ switch(p) {
+ case 0:
+ var r3 = obj.network.simple_request('MARC_XML_RECORD_UPDATE', [ ses(), r.payload.dup_record, new_marcxml, biblio_source ]);
+ if (typeof r3.ilsevent != 'undefined') {
+ throw(r3);
+ } else {
+ alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_overlay'));
+ return {
+ 'id' : r3.id(),
+ 'on_complete' : function() {
+ try {
+ obj.replace_tab_with_opac(r3.id());
+ } catch(E) {
+ alert(E);
}
- break;
- case 1:
- var r2 = obj.network.request(
- api.MARC_XML_RECORD_IMPORT.app,
- api.MARC_XML_RECORD_IMPORT.method + '.override',
- [ ses(), new_marcxml, biblio_source ]
- );
- if (typeof r2.ilsevent != 'undefined') {
- throw(r2);
- } else {
- alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import_with_new_tcn'));
- return {
- 'id' : r2.id(),
- 'on_complete' : function() {
- try {
- obj.replace_tab_with_opac(r2.id());
- } catch(E) {
- alert(E);
- }
- }
- };
+ }
+ };
+ }
+ break;
+ case 1:
+ var r2 = obj.network.request(
+ api.MARC_XML_RECORD_IMPORT.app,
+ api.MARC_XML_RECORD_IMPORT.method + '.override',
+ [ ses(), new_marcxml, biblio_source ]
+ );
+ if (typeof r2.ilsevent != 'undefined') {
+ throw(r2);
+ } else {
+ alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import_with_new_tcn'));
+ return {
+ 'id' : r2.id(),
+ 'on_complete' : function() {
+ try {
+ obj.replace_tab_with_opac(r2.id());
+ } catch(E) {
+ alert(E);
}
- break;
- case 2:
- default:
- alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_cancelled'));
- break;
- }
- break;
- default:
- throw(r);
- break;
- }
- } else {
- alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import'));
- return {
- 'id' : r.id(),
- 'on_complete' : function() {
- try {
- obj.replace_tab_with_opac(r.id());
- } catch(E) {
- alert(E);
- }
+ }
+ };
}
- };
+ break;
+ case 2:
+ default:
+ alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_cancelled'));
+ break;
}
+ break;
+ default:
+ throw(r);
+ break;
+ }
+ } else {
+ alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.successful_import'));
+ return {
+ 'id' : r.id(),
+ 'on_complete' : function() {
+ try {
+ obj.replace_tab_with_opac(r.id());
+ } catch(E) {
+ alert(E);
+ }
+ }
+ };
+ }
+ } catch(E) {
+ obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_error'),E);
+ }
+ };
+
+ if ( $('marc_editor').checked ) {
+ xulG.new_tab(
+ xulG.url_prefix(urls.XUL_MARC_EDIT),
+ { 'tab_name' : 'MARC Editor' },
+ {
+ 'record' : { 'marc' : my_marcxml, "rtype": "bre" },
+ 'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
+ try {
+ JSAN.use('cat.util'); return cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
} catch(E) {
- obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor.import_error'),E);
+ alert(E);
}
+ },
+ 'save' : {
+ 'label' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor.save_button_label'),
+ 'func' : save_marc
}
- }
- }
- );
+ }
+ );
+ } else {
+ save_marc(my_marcxml);
+ }
},
'confirm_overlay' : function(record_ids) {
return;
}
- xulG.new_tab(
- xulG.url_prefix(urls.XUL_MARC_EDIT),
- { 'tab_name' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.tab_name') },
- {
- 'record' : { 'marc' : my_marcxml },
- 'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
+ function overlay_marc (new_marcxml) {
+ try {
+ if (! obj.confirm_overlay( [ obj.data.marked_record ] ) ) { return; }
+ var r = obj.network.simple_request('MARC_XML_RECORD_REPLACE', [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]);
+ if (typeof r.ilsevent != 'undefined') {
+ switch(Number(r.ilsevent)) {
+ case 1704 /* TCN_EXISTS */ :
+ var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.same_tcn', [r.payload.tcn]);
+ var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.import_collision');
+ var btn1 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn1_overlay', [r.payload.new_tcn]);
+ if (btn1) {
+ var robj = obj.network.simple_request(
+ 'PERM_CHECK',[
+ ses(),
+ obj.data.list.au[0].id(),
+ obj.data.list.au[0].ws_ou(),
+ [ 'ALLOW_ALT_TCN' ]
+ ]
+ );
+ if (typeof robj.ilsevent != 'undefined') {
+ obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.permission_error'),E);
+ }
+ if (robj.length != 0) btn1 = null;
+ }
+ var btn2 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn2_cancel');
+ var p = obj.error.yns_alert(msg,title,btn1,btn2,null, $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.confirm_action'));
+ obj.error.sdump('D_ERROR','option ' + p + 'chosen');
+ switch(p) {
+ case 0:
+ var r2 = obj.network.request(
+ api.MARC_XML_RECORD_REPLACE.app,
+ api.MARC_XML_RECORD_REPLACE.method + '.override',
+ [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]
+ );
+ if (typeof r2.ilsevent != 'undefined') {
+ throw(r2);
+ } else {
+ alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.successful_overlay_with_new_TCN'));
+ return {
+ 'id' : r2.id(),
+ 'on_complete' : function() {
+ try {
+ obj.replace_tab_with_opac(r2.id());
+ } catch(E) {
+ alert(E);
+ }
+ }
+ };
+ }
+ break;
+ case 1:
+ default:
+ alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.cancelled_overlay'));
+ break;
+ }
+ break;
+ default:
+ throw(r);
+ break;
+ }
+ } else {
+ alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.success_overlay'));
try {
- JSAN.use('cat.util'); cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
+ obj.data.marked_record_mvr = null;
+ obj.data.marked_record = null;
+ obj.data.stash('marked_record');
+ obj.data.stash('marked_record_mvr');
+ obj.controller.view.marc_import_overlay.disabled = true;
+ if ($("overlay_tcn_indicator")) {
+ $("overlay_tcn_indicator").setAttribute('value',$("catStrings").getString('staff.cat.z3950.marked_record_for_overlay_indicator.no_record.label'));
+ }
+ xulG.set_statusbar(1, $("catStrings").getString('staff.cat.z3950.marked_record_for_overlay_indicator.no_record.label') );
} catch(E) {
- alert(E);
+ dump('Error in z3950.js, post-overlay: ' + E + '\n');
}
- },
- 'save' : {
- 'label' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_record_label'),
- 'func' : function (new_marcxml) {
- try {
- if (! obj.confirm_overlay( [ obj.data.marked_record ] ) ) { return; }
- var r = obj.network.simple_request('MARC_XML_RECORD_REPLACE', [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]);
- if (typeof r.ilsevent != 'undefined') {
- switch(Number(r.ilsevent)) {
- case 1704 /* TCN_EXISTS */ :
- var msg = $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.same_tcn', [r.payload.tcn]);
- var title = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.import_collision');
- var btn1 = typeof r.payload.new_tcn == 'undefined' ? null : $("catStrings").getFormattedString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn1_overlay', [r.payload.new_tcn]);
- if (btn1) {
- var robj = obj.network.simple_request(
- 'PERM_CHECK',[
- ses(),
- obj.data.list.au[0].id(),
- obj.data.list.au[0].ws_ou(),
- [ 'ALLOW_ALT_TCN' ]
- ]
- );
- if (typeof robj.ilsevent != 'undefined') {
- obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.permission_error'),E);
- }
- if (robj.length != 0) btn1 = null;
- }
- var btn2 = $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.btn2_cancel');
- var p = obj.error.yns_alert(msg,title,btn1,btn2,null, $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.confirm_action'));
- obj.error.sdump('D_ERROR','option ' + p + 'chosen');
- switch(p) {
- case 0:
- var r2 = obj.network.request(
- api.MARC_XML_RECORD_REPLACE.app,
- api.MARC_XML_RECORD_REPLACE.method + '.override',
- [ ses(), obj.data.marked_record, new_marcxml, biblio_source ]
- );
- if (typeof r2.ilsevent != 'undefined') {
- throw(r2);
- } else {
- alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.successful_overlay_with_new_TCN'));
- return {
- 'id' : r2.id(),
- 'on_complete' : function() {
- try {
- obj.replace_tab_with_opac(r2.id());
- } catch(E) {
- alert(E);
- }
- }
- };
- }
- break;
- case 1:
- default:
- alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.cancelled_overlay'));
- break;
- }
- break;
- default:
- throw(r);
- break;
- }
- } else {
- alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.success_overlay'));
- return {
- 'id' : r.id(),
- 'on_complete' : function() {
- try {
- obj.replace_tab_with_opac(r.id());
- } catch(E) {
- alert(E);
- }
- }
- };
+ return {
+ 'id' : r.id(),
+ 'on_complete' : function() {
+ try {
+ obj.replace_tab_with_opac(r.id());
+ } catch(E) {
+ alert(E);
}
+ }
+ };
+ }
+ } catch(E) {
+ obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_error'),E);
+ }
+ }
+
+ if ( $('marc_editor').checked ) {
+ xulG.new_tab(
+ xulG.url_prefix(urls.XUL_MARC_EDIT),
+ { 'tab_name' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.tab_name') },
+ {
+ 'record' : { 'marc' : my_marcxml },
+ 'fast_add_item' : function(doc_id,cn_label,cp_barcode) {
+ try {
+ JSAN.use('cat.util'); cat.util.fast_item_add(doc_id,cn_label,cp_barcode);
} catch(E) {
- obj.error.standard_unexpected_error_alert($("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_error'),E);
+ alert(E);
}
+ },
+ 'save' : {
+ 'label' : $("catStrings").getString('staff.cat.z3950.spawn_marc_editor_for_overlay.overlay_record_label'),
+ 'func' : overlay_marc
}
- }
- }
- );
+ }
+ );
+ } else {
+ overlay_marc(my_marcxml);
+ }
},
<button id="search" label="&staff.cat.z3950.search.label;" accesskey="&staff.cat.z3950.search.accesskey;" disabled="true"/>
</hbox>
</groupbox>
- <splitter id="x_splitter" collapse="after" oils_persist="state hidden"><grippy id="splitter_grippy1"/></splitter>
+ <splitter id="x_splitter" collapse="after" oils_persist="state hidden" oils_persist_peers="x_splitter1 x_splitter2"><grippy id="splitter_grippy1"/></splitter>
<groupbox id="x_splitter2" oils_persist="width" flex="1">
<caption label="&staff.cat.z3950.service_credentials.label;"/>
<grid flex="1">
</hbox>
</groupbox>
</hbox>
- <splitter id="z_splitter" collapse="before" oils_persist="state hidden"><grippy id="splitter_grippy2"/></splitter>
+ <splitter id="z_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="top_pane z_splitter2"><grippy id="splitter_grippy2"/></splitter>
<groupbox id="z_splitter2" oils_persist="height" flex="1">
<caption label="&staff.cat.z3950.results_caption.label;"/>
<hbox>
</button>
<spacer flex="1"/>
<button id="marc_view_btn" command="marc_view" disabled="true"/>
+ <checkbox id="marc_editor" label="&staff.cat.z3950.marc_editor.label;" accesskey="&staff.cat.z3950.marc_editor.accesskey;" oils_persist="checked" checked="true"/>
<button id="marc_import_overlay" label="&staff.cat.z3950.marc_import_overlay.label;" accesskey="&staff.cat.z3950.marc_import_overlay.accesskey;" disabled="true"/>
<button id="marc_import" label="&staff.cat.z3950.result_message.marc_import.label;" accesskey="&staff.cat.z3950.result_message.marc_import.accesskey;" disabled="true"/>
<button id="toggle_form_btn" command="toggle_form"/>
</hbox>
- <deck id="deck" flex="1">
- <tree id="results" flex="1" enableColumnDrag="true" seltype="single"/>
- <iframe id="marc_frame" src="/xul/server/cat/marc_view.html" flex="1"/>
- </deck>
+ <hbox flex="1">
+ <image id="jacket_image" oils_persist="width"/>
+ <splitter id="jacket_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="jacket_image deck"><grippy id="jacket_splitter_grippy" /></splitter>
+ <deck id="deck" flex="1" oils_persist="width">
+ <tree id="results" flex="1" enableColumnDrag="true" seltype="single"/>
+ <iframe id="marc_frame" src="/xul/server/cat/marc_view.html" flex="1"/>
+ </deck>
+ </hbox>
</groupbox>
</groupbox>
var error;
var network;
var data;
+var transit_list;
+var hold_list;
function my_init() {
try {
);
}
+ JSAN.use('circ.util');
+ JSAN.use('util.list');
+
+ var columns = circ.util.transit_columns({});
+ transit_list = new util.list('transit');
+ transit_list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
+
+ hold_list = new util.list('hold');
+ hold_list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
+
// timeout so xulG gets a chance to get pushed in
setTimeout(
function () { xulG.from_item_details_new = false; load_item(); },
set("copy_circ_lib" , typeof details.copy.circ_lib() == 'object' ? details.copy.circ_lib().shortname() : data.hash.aou[ details.copy.circ_lib() ].shortname());
set_tooltip("copy_circ_lib" , typeof details.copy.circ_lib() == 'object' ? details.copy.circ_lib().name() : data.hash.aou[ details.copy.circ_lib() ].name());
var cm = details.copy.circ_modifier();
- set("circ_modifier", document.getElementById('commonStrings').getFormattedString('staff.circ_modifier.display',[cm,data.hash.ccm[cm].name(),data.hash.ccm[cm].description()]));
+ if (typeof data.hash.ccm[cm] != 'undefined') {
+ set("circ_modifier", document.getElementById('commonStrings').getFormattedString('staff.circ_modifier.display',[cm,data.hash.ccm[cm].name(),data.hash.ccm[cm].description()]));
+ } else {
+ set("circ_modifier","");
+ }
set("circulate", get_localized_bool( details.copy.circulate() ));
set("copy_number", details.copy.copy_number());
set("copy_create_date", util.date.formatted_date( details.copy.create_date(), '%{localized}' ));
set("hold_transit_copy", '');
if (details.transit) {
- JSAN.use('circ.util'); var columns = circ.util.transit_columns({});
- JSAN.use('util.list'); var list = new util.list('transit');
- list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
- list.append( { 'row' : { 'my' : { 'atc' : details.transit, } } });
+ transit_list.clear();
+ transit_list.append( { 'row' : { 'my' : { 'atc' : details.transit, } } });
var transit_copy_status = typeof details.transit.copy_status() == 'object' ? details.transit.copy_status() : data.hash.ccs[ details.transit.copy_status() ];
set("transit_copy_status", transit_copy_status.name() );
}
);
- JSAN.use('util.list'); var list = new util.list('hold');
- list.init( { 'columns' : columns, 'map_row_to_columns' : circ.util.std_map_row_to_columns(), });
- list.append( { 'row' : { 'my' : { 'ahr' : better_fleshed_hold_blob.hold, 'acp' : details.copy, 'status' : status_robj, } } });
+ hold_list.clear();
+ hold_list.append( { 'row' : { 'my' : { 'ahr' : better_fleshed_hold_blob.hold, 'acp' : details.copy, 'status' : status_robj, } } });
JSAN.use('patron.util');
var au_obj = patron.util.retrieve_fleshed_au_via_id( ses(), details.hold.usr() );
<?xul-overlay href="/xul/server/cat/bib_brief_overlay.xul"?>
<window id="alt_copy_summary_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
</grid>
</tabpanel>
<tabpanel orient="vertical"><!-- Hold/Transit -->
- <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;">
+ <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;" oils_persist="height">
<caption id="hold_caption" label="&staff.circ.copy_details.hold_caption;"/>
<label id="hold_patron_name" class="patronNameLarge"/>
<tree id="hold" flex="1" enableColumnDrag="true"/>
<spacer FIXME="label and tree get swapped without this"/>
</groupbox>
- <splitter><grippy/></splitter>
- <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;">
+ <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="holds transits"><grippy/></splitter>
+ <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;" oils_persist="height">
<caption id="transit_caption" label="&staff.circ.copy_details.transit_caption;"/>
<tree id="transit" flex="1" enableColumnDrag="true"/>
</groupbox>
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="circ_circ_brief_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="circ_circ_brief_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
- width="750" height="550"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+ width="750" height="550" oils_persist="height width sizemode"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
</script>
<vbox flex="1" class="my_overflow">
- <vbox id="top_vbox" flex="1" class="my_overflow"/>
- <splitter><grippy/></splitter>
- <vbox id="mid_vbox" flex="1" class="my_overflow"/>
- <splitter><grippy/></splitter>
- <groupbox flex="1" id="circs" class="my_overflow">
+ <vbox id="top_vbox" flex="1" class="my_overflow" oils_persist="height"/>
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="top_vbox mid_vbox"><grippy/></splitter>
+ <vbox id="mid_vbox" flex="1" class="my_overflow" oils_persist="height"/>
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="mid_vbox circs"><grippy/></splitter>
+ <groupbox flex="1" id="circs" class="my_overflow" oils_persist="height">
<caption label="&staff.circ.circ_summary.caption;"/>
</groupbox>
<hbox>
<messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties" />
<vbox flex="1" style="overflow: auto;">
- <vbox id="top_box" flex="1" style="border: none; overflow: none; min-height: 80;"/>
- <splitter><grippy/></splitter>
- <vbox id="item_summary_box" flex="1" style="border: none; overflow: none; min-height: 80;"/>
- <splitter><grippy/></splitter>
- <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;">
+ <vbox id="top_box" flex="1" style="border: none; overflow: none; min-height: 80;" oils_persist="height"/>
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="top_box item_summary_box"><grippy/></splitter>
+ <vbox id="item_summary_box" flex="1" style="border: none; overflow: none; min-height: 80;" oils_persist="height"/>
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="item_summary_box holds"><grippy/></splitter>
+ <groupbox flex="1" id="holds" style="overflow: none; min-height: 80;" oils_persist="height">
<caption id="hold_caption" label="&staff.circ.copy_details.hold_caption;"/>
<label id="patron_name" class="patronNameLarge"/>
<tree id="hold" flex="1" enableColumnDrag="true"/>
<spacer FIXME="label and tree get swapped without this"/>
</groupbox>
- <splitter><grippy/></splitter>
- <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;">
+ <splitter id="splitter3" oils_persist="state hidden" oils_persist_peers="holds transits"><grippy/></splitter>
+ <groupbox flex="1" id="transits" style="overflow: none; min-height: 80;" oils_persist="height">
<caption id="transit_caption" label="&staff.circ.copy_details.transit_caption;"/>
<tree id="transit" flex="1" enableColumnDrag="true"/>
</groupbox>
- <splitter><grippy/></splitter>
- <groupbox flex="1" id="circs" style="overflow: none; min-height: 80;">
+ <splitter id="splitter4" oils_persist="state hidden" oils_persist_peers="transits circs"><grippy/></splitter>
+ <groupbox flex="1" id="circs" style="overflow: none; min-height: 80;" oils_persist="height">
<caption id="circ_caption" label="&staff.circ.copy_details.circ_caption;" style="font-weight: bold"/>
<vbox id="circ_box" flex="1" style="min-height: 80"/>
</groupbox>
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="main_test_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="pre_cat_fields" title="&staff.circ.pre_cat.window.title;"
- orient="vertical" style="overflow: auto"
- onload="try{my_init();font_helper(); }catch(E){alert(E);}"
+ orient="vertical" style="overflow: auto" oils_persist="height width sizemode"
+ onload="try{ my_init(); font_helper(); persist_helper(); }catch(E){alert(E);}"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="print_list_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
'editable' : false, 'render' : function(my) { return my.patron_family_name ? my.patron_family_name : ""; }
},
{
+ "persist": "hidden width ordinal",
+ "id": "patron_alias",
+ 'label' : document.getElementById('circStrings').getString('staff.circ.utils.patron_alias'),
+ 'flex' : 1,
+ 'primary' : false,
+ 'hidden' : true,
+ 'editable' : false, 'render' : function(my) { return my.patron_alias ? my.patron_alias : ""; }
+ },
+ {
'persist' : 'hidden width ordinal',
'id' : 'patron_first_given_name',
'label' : document.getElementById('circStrings').getString('staff.circ.utils.patron_first_given_name'),
'route_to' : '',
'route_to_msg' : '',
'route_to_org_fullname' : '',
+ 'destination_shelf' : '',
+ 'destination_shelf_msg' : '',
'courier_code' : '',
'street1' : '',
'street2' : '',
if (behind_the_desk_support) {
var usr_settings = network.simple_request('FM_AUS_RETRIEVE',[ses(),check.payload.hold.usr()]);
if (typeof usr_settings['circ.holds_behind_desk'] != 'undefined') {
- print_data.prefer_behind_holds_desk = true;
- check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.private_hold_shelf');
- print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
- print_data.route_to = check.route_to;
+ if (usr_settings['circ.holds_behind_desk']) {
+ print_data.prefer_behind_holds_desk = true;
+ check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.private_hold_shelf');
+ print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
+ print_data.route_to = check.route_to;
+ } else {
+ check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
+ print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
+ print_data.route_to = check.route_to;
+ }
} else {
check.route_to = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
print_data.route_to_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
print_data.route_to = check.route_to;
}
}
+ print_data.destination_shelf_msg = print_data.route_to_msg;
+ print_data.destination_shelf = print_data.route_to;
msg += print_data.route_to_msg;
msg += '\n';
}
JSAN.use('patron.util');
var au_obj = patron.util.retrieve_fleshed_au_via_id( session, check.payload.hold.usr() );
print_data.user = au_obj;
+ print_data.user_stat_cat_entries = [];
+ var entries = au_obj.stat_cat_entries();
+ for (var i = 0; i < entries.length; i++) {
+ var stat_cat = data.hash.my_actsc[ entries[i].stat_cat() ];
+ if (!stat_cat) {
+ stat_cat = data.lookup('actsc', entries[i].stat_cat());
+ }
+ print_data.user_stat_cat_entries.push( {
+ 'id' : entries[i].id(),
+ 'stat_cat' : {
+ 'id' : stat_cat.id(),
+ 'name' : stat_cat.name(),
+ 'opac_visible' : stat_cat.opac_visible(),
+ 'owner' : stat_cat.owner(),
+ 'usr_summary' : stat_cat.usr_summary()
+ },
+ 'stat_cat_entry' : entries[i].stat_cat_entry(),
+ 'target_usr' : entries[i].target_usr()
+ } );
+ }
msg += '\n';
if (au_obj.alias()) {
print_data.hold_for_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.hold.patron_alias', [au_obj.alias()]);
print_data.request_date_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.hold.request_date', [print_data.request_date]);
msg += print_data.request_date_msg;
msg += '\n';
+ var destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.hold_shelf');
+ print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+ print_data.destination_shelf = destination_shelf;
+ var behind_the_desk_support = String( data.hash.aous['circ.holds.behind_desk_pickup_supported'] ) == 'true';
+ if (behind_the_desk_support) {
+ var usr_settings = network.simple_request('FM_AUS_RETRIEVE',[ses(),check.payload.hold.usr()]);
+ if (typeof usr_settings['circ.holds_behind_desk'] != 'undefined') {
+ if (usr_settings['circ.holds_behind_desk']) {
+ print_data.prefer_behind_holds_desk = true;
+ destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.private_hold_shelf');
+ print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+ print_data.destination_shelf = destination_shelf;
+ } else {
+ destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
+ print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+ print_data.destination_shelf = destination_shelf;
+ }
+ } else {
+ destination_shelf = document.getElementById('circStrings').getString('staff.circ.route_to.public_hold_shelf');
+ print_data.destination_shelf_msg = document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [destination_shelf]);
+ print_data.destination_shelf = destination_shelf;
+ }
+ }
}
var rv = 0;
var suppress_popups = data.hash.aous['ui.circ.suppress_checkin_popups'];
staff.cat.record_buckets.new_bucket.bucket_prompt_title=Bucket Creation
staff.cat.record_buckets.new_bucket.same_name_alert=You already have a bucket with that name.
staff.cat.record_buckets.new_bucket.bucket_created=Bucket %1$s created.
-staff.cat.record_buckets.merge_records.merge_lead=Merge these records? (Select the "lead" record first)
-staff.cat.record_buckets.merge_records.button.label=Merge
-staff.cat.record_buckets.merge_records.cancel_button.label=Cancel
-staff.cat.record_buckets.merge_records.cancel_button.accesskey=C
-staff.cat.record_buckets.merge_records.lead_record_number=Lead Record? # %1$s
-staff.cat.record_buckets.merge_records.lead=Lead
staff.cat.record_buckets.merge_records.fancy_prompt_title=Record Merging
-staff.cat.record_buckets.merge_records.fancy_prompt.alert=Merge Aborted
-staff.cat.record_buckets.merge_records.success=Records were successfully merged.
-staff.cat.record_buckets.merge_records.catch.std_unex_error=Records were not likely merged.
staff.cat.record_buckets.delete_records.xml1=Delete these records?
staff.cat.record_buckets.delete_records.button.label=Delete
staff.cat.record_buckets.delete_records.cancel_button.label=Cancel
staff.cat.z3950.handle_results.result_error=Error retrieving results.
staff.cat.z3950.handle_results.search_result_error=Failure during search result handling.
staff.cat.z3950.replace_tab_with_opac.tab_name=Retrieving title...
+staff.cat.z3950.spawn_marc_editor.save_button_label=Import Record
staff.cat.z3950.spawn_marc_editor.same_tcn=A record with TCN %1$s already exists.\nFIXME: add record summary here
staff.cat.z3950.spawn_marc_editor.title=Import Collision
staff.cat.z3950.spawn_marc_editor.btn1_overlay=Overlay
staff.circ.utils.author.none=No Author?
staff.circ.utils.notify_time=Last Notify Time
staff.circ.utils.notify_count=Notices
+staff.circ.utils.patron_alias=Patron Alias
staff.circ.utils.patron_family_name=Patron Last Name
staff.circ.utils.patron_first_given_name=Patron First Name
staff.circ.utils.checkin.override=Override Checkin Failure?
}
g.open_menu = function() {
- try {
- netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
- var mframe = xulG.window.open(urls.XUL_MENU_FRAME
- + '?server='+window.escape(xulG.url),
- 'main'+xulG.window.window_name_increment(),'chrome,resizable'
- );
- delete xulG['_sound']; // This came from util.error but I want menu.js to have its own
- mframe.xulG = xulG; // This is the xulG from main.js, with auth, url, and window
- } catch(E) {
- alert(E);
- }
+ delete xulG['_sound'];
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
+ getService(Components.interfaces.nsIWindowMediator);
+ var eg_main = wm.getMostRecentWindow('eg_main');
+ eg_main.openTabs.unshift('init');
+ wm.getMostRecentWindow('eg_main').new_tabs(null);
}
g.data.init();
<!-- OVERLAYS -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
-<window id="simple_auth_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="simple_auth_win" oils_persist="height width sizemode"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<!-- OVERLAYS -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
-<window id="verify_win" onload="try { verify_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="verify_win" onload="try { verify_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_barcode_entry_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<vbox flex="1" class="my_overflow">
<groupbox orient="vertical" flex="1">
<caption id="caption" label="&staff.patron.bill_interface.caption.label;"/>
- <hbox>
+ <hbox id="before_splitter1" oils_persist="height">
<grid flex="1">
<columns flex="1">
<column/>
<label value="&staff.patron.bills_overlay.payment_type.value;" class="emphasis1" accesskey="&staff.patron.bills_overlay.payment_type.accesskey;" control="payment_type"/>
<menulist id="payment_type">
<menupopup id="payment_type_menupopup">
- <menuitem id="payment_type_menuitem1" label="&staff.patron.bills_overlay.cash.label;" value="cash_payment"/>
- <menuitem id="payment_type_menuitem2" label="&staff.patron.bills_overlay.check.label;" value="check_payment"/>
- <menuitem id="payment_type_menuitem3" label="&staff.patron.bills_overlay.credit_card.label;" value="credit_card_payment"/>
+ <menuitem id="payment_type_menuitem1" class="hide_patron_cash" label="&staff.patron.bills_overlay.cash.label;" value="cash_payment"/>
+ <menuitem id="payment_type_menuitem2" class="hide_patron_check" label="&staff.patron.bills_overlay.check.label;" value="check_payment"/>
+ <menuitem id="payment_type_menuitem3" class="hide_patron_credit_card" label="&staff.patron.bills_overlay.credit_card.label;" value="credit_card_payment"/>
<menuitem id="payment_type_menuitem4" class="hide_patron_credit" label="&staff.patron.bills_overlay.patron_credit.label;" value="credit_payment" />
- <menuitem id="payment_type_menuitem5" label="&staff.patron.bills_overlay.word.label;" value="work_payment"/>
- <menuitem id="payment_type_menuitem6" label="&staff.patron.bills_overlay.forgive.label;" value="forgive_payment"/>
- <menuitem id="payment_type_menuitem7" label="&staff.patron.bills_overlay.goods.label;" value="goods_payment"/>
+ <menuitem id="payment_type_menuitem5" class="hide_patron_work" label="&staff.patron.bills_overlay.word.label;" value="work_payment"/>
+ <menuitem id="payment_type_menuitem6" class="hide_patron_forgive" label="&staff.patron.bills_overlay.forgive.label;" value="forgive_payment"/>
+ <menuitem id="payment_type_menuitem7" class="hide_patron_goods" label="&staff.patron.bills_overlay.goods.label;" value="goods_payment"/>
</menupopup>
</menulist>
</row>
</hbox>
</groupbox>
</hbox>
- <splitter />
- <vbox flex="1">
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1" />
+ <vbox flex="1" id="after_splitter1" oils_persist="height">
<hbox>
<button id="bill_patron_btn" label="&staff.patron.bills_overlay.bill_patron.label;" accesskey="&staff.patron.bills_overlay.bill_patron.accesskey;" />
<button id="bill_history_btn" label="&staff.patron.bills_overlay.history.label;" accesskey="&staff.patron.bills_overlay.history.accesskey;" />
</button>
</hbox>
</vbox>
- <splitter />
- <hbox>
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_splitter1 after_splitter2" />
+ <hbox id="after_splitter2" oils_persist="height">
<vbox>
<hbox>
<label value='&staff.patron.bill_interface.voided_this_session.label;' class="emphasis1"/><label id="currently_voided" value="0.00"/>
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_bill" title="&staff.patron.bill_cc_info.title;"
- orient="vertical" style="overflow: auto"
- onload="try{info_init(); font_helper();refresh_fields();}catch(E){alert(E);}"
+ orient="vertical" style="overflow: auto" oils_persist="height width sizemode"
+ onload="try{info_init(); font_helper(); refresh_fields(); persist_helper(); }catch(E){alert(E);}"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_bill" title="&staff.patron.bill_check_info.title;"
- orient="vertical" style="overflow: auto"
- onload="try{info_init(); font_helper();}catch(E){alert(E);}"
+ orient="vertical" style="overflow: auto" oils_persist="height width sizemode"
+ onload="try{info_init(); font_helper(); persist_helper(); }catch(E){alert(E);}"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<?xul-overlay href="/xul/server/patron/bill_summary_overlay.xul"?>
-<window id="bill_details_win" width="700" height="550"
- onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="bill_details_win" width="700" height="550" oils_persist="width height sizemode"
+ onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<vbox flex="1" class="my_overflow">
<label id="patron_name" class="patronNameLarge"/>
- <groupbox orient="vertical" flex="1" id="summary" />
+ <groupbox orient="vertical" flex="1" id="summary" oils_persist="height"/>
- <splitter><grippy/></splitter>
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="summary copy_summary_vbox"><grippy/></splitter>
<vbox id="copy_summary_vbox" flex="1" />
- <splitter id="copy_summary_splitter"><grippy/></splitter>
+ <splitter id="copy_summary_splitter" oils_persist="state hidden" oils_persist_peers="copy_summary_vbox after_copy_summary_splitter"><grippy/></splitter>
- <groupbox orient="vertical" flex="2">
+ <groupbox id="after_copy_summary_splitter" oils_persist="height" orient="vertical" flex="2">
<caption label="&staff.patron.bill_details.bills.label;" style="color: red"/>
<tree id="bill_tree" flex="1" enableColumnDrag="true"/>
<hbox>
</hbox>
</groupbox>
- <splitter><grippy/></splitter>
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_copy_summary_splitter after_splitter2"><grippy/></splitter>
- <groupbox orient="vertical" flex="2">
+ <groupbox orient="vertical" flex="2" id="after_splitter2" oils_persist="height">
<caption label="&staff.patron.bill_details.payments.label;" style="color: green"/>
<tree id="payment_tree" flex="1" enableColumnDrag="true"/>
<hbox>
<?xul-overlay href="/xul/server/patron/bill_summary_overlay.xul"?>
<window id="patron_bill" title="&staff.patron.bill_wizard.title;"
- orient="vertical" style="overflow: auto"
- onload="try { patron_bill_init(); font_helper(); } catch(E) { alert(E); }" width="700" height="550"
+ orient="vertical" style="overflow: auto" oils_persist="width height sizemode"
+ onload="try { patron_bill_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" width="700" height="550"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<hbox id="left_deck_vbox" flex="1" oils_persist="height">
<deck id="patron_left_deck" oils_persist="height"/>
</hbox>
- <splitter id="deck_splitter" collapse="before" oils_persist="state hidden"><grippy id="splitter_grippy"/></splitter>
+ <splitter id="deck_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="left_deck_vbox right_deck_vbox"><grippy id="splitter_grippy"/></splitter>
<hbox id="right_deck_vbox" flex="3" oils_persist="height">
<deck id="patron_right_deck" oils_persist="height"/>
</hbox>
<vbox id="left_deck_vbox" flex="1" oils_persist="width">
<deck id="patron_left_deck" oils_persist="width"/>
</vbox>
- <splitter id="deck_splitter" collapse="before" oils_persist="state hidden"><grippy id="splitter_grippy"/></splitter>
+ <splitter id="deck_splitter" collapse="before" oils_persist="state hidden" oils_persist_peers="left_deck_vbox right_deck_vbox"><grippy id="splitter_grippy"/></splitter>
<vbox id="right_deck_vbox" flex="3" oils_persist="width">
<deck id="patron_right_deck" oils_persist="width"/>
</vbox>
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="edit_penalty_win"
- onload="try { edit_penalty_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { edit_penalty_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+ oils_persist="height width sizemode"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&staff.patron_display.edit_penalty_dialog.title;">
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="hold_cancel_win"
- onload="try { hold_cancel_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { hold_cancel_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&staff.hold_list.cancel_hold_dialog.title;">
if (xulG.ahr_id) fetch_and_render_all();
+ if (xul_param('when_done')) {
+ xul_param('when_done')();
+ }
+
} catch(E) {
try { g.error.standard_unexpected_error_alert('/xul/server/patron/hold_notices.xul',E); } catch(E) { alert('FIXME: ' + js2JSON(E)); }
}
},
}
);
+ dump('hold details init_list done\n');
}
function a_list_of_one() {
- g.list.clear();
- g.list.append(
- {
- 'row' : {
- 'my' : {
- 'ahr' : g.ahr,
- 'status' : g.blob.status,
- 'acp' : g.blob.copy,
- 'acn' : g.blob.volume,
- 'mvr' : g.blob.mvr,
- 'patron_family_name' : g.blob.patron_last,
- 'patron_first_given_name' : g.blob.patron_first,
- 'patron_barcode' : g.blob.patron_barcode,
- 'total_holds' : g.blob.total_holds,
- 'queue_position' : g.blob.queue_position,
- 'potential_copies' : g.blob.potential_copies,
- 'estimated_wait' : g.blob.estimated_wait,
- 'ahrn_count' : g.blob.hold.notes().length,
- 'blob' : g.blob
- }
- },
- 'no_auto_select' : true,
- }
- );
+ try {
+ g.list.clear();
+ g.list.append(
+ {
+ 'row' : {
+ 'my' : {
+ 'ahr' : g.ahr,
+ 'status' : g.blob.status,
+ 'acp' : g.blob.copy,
+ 'acn' : g.blob.volume,
+ 'mvr' : g.blob.mvr,
+ 'patron_family_name' : g.blob.patron_last,
+ 'patron_first_given_name' : g.blob.patron_first,
+ 'patron_barcode' : g.blob.patron_barcode,
+ 'patron_alias' : g.blob.patron_alias,
+ 'total_holds' : g.blob.total_holds,
+ 'queue_position' : g.blob.queue_position,
+ 'potential_copies' : g.blob.potential_copies,
+ 'estimated_wait' : g.blob.estimated_wait,
+ 'ahrn_count' : g.blob.hold.notes().length,
+ 'blob' : g.blob
+ }
+ },
+ 'no_auto_select' : true,
+ }
+ );
+ } catch(E) {
+ alert('Error in hold_details.js, a_list_of_one(): ' + E);
+ }
}
function retrieve_notifications() {
<vbox id="bib_brief_box" flex="1" style="min-height: 10em;"/>
</vbox>
- <splitter><grippy/></splitter>
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="v1 v2"><grippy/></splitter>
<vbox id="v2" flex="1" oils_persist="height">
<vbox flex="1">
</vbox>
</vbox>
- <splitter><grippy/></splitter>
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="v2 after_splitter2"><grippy/></splitter>
- <tabbox flex="1">
+ <tabbox flex="1" id="after_splitter2" oils_persist="height">
<tabs>
<tab label="&staff.patron.holds.notes_tab.label;" accesskey="&staff.patron.holds.notes_tab.accesskey;" />
<tab label="&staff.patron.holds.notices_tab.label;" accesskey="&staff.patron.holds.notices_tab.accesskey;" />
row.my.patron_family_name = blob.patron_last;
row.my.patron_first_given_name = blob.patron_first;
row.my.patron_barcode = blob.patron_barcode;
+ row.my.patron_alias = blob.patron_alias;
row.my.total_holds = blob.total_holds;
row.my.queue_position = blob.queue_position;
row.my.potential_copies = blob.potential_copies;
obj.controller.view.cmd_holds_edit_request_date.setAttribute('disabled','false');
obj.controller.view.cmd_holds_activate.setAttribute('disabled','false');
obj.controller.view.cmd_holds_suspend.setAttribute('disabled','false');
- obj.controller.view.cmd_alt_view.setAttribute('disabled','false');
+ obj.controller.view.cmd_alt_view.setAttribute('rendering_rows','false');
+ if (obj.controller.view.cmd_alt_view.getAttribute('ready')=='true') {
+ obj.controller.view.cmd_alt_view.setAttribute('disabled','false');
+ }
obj.controller.view.cmd_holds_retarget.setAttribute('disabled','false');
obj.controller.view.cmd_holds_cancel.setAttribute('disabled','false');
obj.controller.view.cmd_holds_uncancel.setAttribute('disabled','false');
obj.controller.view.cmd_holds_activate.setAttribute('disabled','true');
obj.controller.view.cmd_holds_suspend.setAttribute('disabled','true');
obj.controller.view.cmd_alt_view.setAttribute('disabled','true');
+ obj.controller.view.cmd_alt_view.setAttribute('rendering_rows','true');
obj.controller.view.cmd_holds_retarget.setAttribute('disabled','true');
obj.controller.view.cmd_holds_cancel.setAttribute('disabled','true');
obj.controller.view.cmd_holds_uncancel.setAttribute('disabled','true');
}
}
],
+ 'cmd_holds_print_alt' : [
+ ['command'],
+ function() {
+ try {
+ var content_params = {
+ "session": ses(),
+ "authtime": ses("authtime"),
+ "no_xulG": false,
+ "show_nav_buttons": true,
+ "show_print_button": false
+ };
+ ["url_prefix", "new_tab", "set_tab",
+ "close_tab", "new_patron_tab",
+ "set_patron_tab", "volume_item_creator",
+ "get_new_session",
+ "holdings_maintenance_tab", "set_tab_name",
+ "open_chrome_window", "url_prefix",
+ "network_meter", "page_meter",
+ "set_statusbar", "set_help_context"
+ ].forEach(function(k) {
+ content_params[k] = xulG[k];
+ });
+
+ var loc = urls.XUL_BROWSER + "?url=" + window.escape(
+ xulG.url_prefix("/opac/extras/circ/alt_holds_print.html").replace("http:","https:")
+ );
+ xulG.new_tab(
+ loc, {
+ "tab_name": "Printable Pull List", /* XXX i18n */
+ "browser": false
+ }, content_params
+ );
+ } catch (E) {
+ g.error.sdump("D_ERROR", E);
+ }
+ }
+ ],
'cmd_holds_print' : [
['command'],
function() {
var x_clear_shelf_widgets = document.getElementById('clear_shelf_widgets');
var x_expired_checkbox = document.getElementById('expired_checkbox');
var x_print_full_pull_list = document.getElementById('print_full_btn');
+ var x_print_full_pull_list_alt = document.getElementById('print_alt_btn');
switch(obj.hold_interface_type) {
case 'shelf':
obj.render_lib_menus({'pickup_lib':true});
if (x_lib_type_menu) x_lib_type_menu.hidden = false;
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
if (x_clear_shelf_widgets) x_clear_shelf_widgets.hidden = false;
+ if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
break;
case 'pull' :
if (x_fetch_more) x_fetch_more.hidden = false;
if (x_print_full_pull_list) x_print_full_pull_list.hidden = false;
+ if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = false;
if (x_lib_type_menu) x_lib_type_menu.hidden = true;
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
break;
obj.render_lib_menus({'pickup_lib':true,'request_lib':true});
if (x_lib_filter_checkbox) x_lib_filter_checkbox.hidden = false;
if (x_lib_type_menu) x_lib_type_menu.hidden = false;
+ if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
break;
default:
if (x_lib_type_menu) x_lib_type_menu.hidden = true;
if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
if (x_show_cancelled_deck) x_show_cancelled_deck.hidden = false;
+ if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
break;
}
setTimeout( // We do this because render_lib_menus above creates and appends a DOM node, but until this thread exits, it doesn't really happen
}, 0
);
+ $('cmd_alt_view').setAttribute('disabled','true');
+ xulG.when_done = function() {
+ $('cmd_alt_view').setAttribute('ready','true');
+ if ($('cmd_alt_view').getAttribute('rendering_rows') != 'true') {
+ $('cmd_alt_view').setAttribute('disabled','false');
+ }
+ dump('hold details UI ready\n');
+ }
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
JSAN.use('util.browser');
obj.browser = new util.browser();
'push_xulG' : true,
'alt_print' : false,
'browser_id' : 'hold_detail_frame',
- 'passthru_content_params' : xulG,
+ 'passthru_content_params' : xulG
}
);
<command id="cmd_csv_to_file" />
<command id="cmd_holds_print" />
<command id="cmd_holds_print_full" />
+ <command id="cmd_holds_print_alt" />
<command id="cmd_show_catalog" />
<command id="cmd_retrieve_patron" />
<command id="cmd_holds_edit_desire_mint_condition" />
accesskey="&staff.circ.holds.title_transfer.accesskey;" />
<command id="cmd_search_opac" />
<command id="save_columns" />
- <command id="cmd_alt_view" />
+ <command id="cmd_alt_view" disabled="true"/>
<command id="cmd_cancelled_holds_view" />
<command id="cmd_uncancelled_holds_view" />
<command id="cmd_view_expired_onshelf_holds"
<button id="holds_print" label="&staff.patron.holds_overlay.print.label;" command="cmd_holds_print" accesskey="&staff.patron.holds_overlay.print.accesskey;" />
<button id="print_full_btn" hidden="true" label="&staff.patron.holds_overlay.print_full_pull_list.label;" command="cmd_holds_print_full" accesskey="&staff.patron.holds_overlay.print_full_pull_list.accesskey;" />
+ <button id="print_alt_btn" hidden="true" label="&staff.patron.holds_overlay.print_alt_pull_list.label;" command="cmd_holds_print_alt" accesskey="&staff.patron.holds_overlay.print_alt_pull_list.accesskey;" />
<spacer flex="1"/>
</hbox>
break;
}
+ var horizontal_interface = String( g.data.hash.aous['ui.circ.patron_summary.horizontal'] ) == 'true';
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
var top_xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: auto"><description>' + second_msg + '</description>';
top_xml += '<hbox><spacer flex="1"/><button label="'+$("patronStrings").getString('staff.patron.info_group.link_patron.move.label')+'"';
top_xml += ' accesskey="'+$("patronStrings").getString('staff.patron.info_group.link_patron.move.accesskey')+'" name="fancy_submit"/>';
top_xml += '<button label="'+$("patronStrings").getString('staff.patron.info_group.link_patron.done.label')+'"';
top_xml += ' accesskey="'+$("patronStrings").getString('staff.patron.info_group.link_patron.done.accesskey')+'" name="fancy_cancel"/></hbox></vbox>';
- var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: vertical"><hbox flex="1">';
+ var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: vertical">';
+ if (horizontal_interface) {
+ xml += '<vbox flex="1">';
+ } else {
+ xml += '<hbox flex="1">';
+ }
/************/
xml += '<vbox flex="1">';
xml += '<hbox><spacer flex="1"/>';
xml += '?show_name=1&id=' + patron_b.id() + '"/>';
xml += '</vbox>';
/************/
- xml += '</hbox></vbox>';
+ if (horizontal_interface) {
+ xml += '</vbox>';
+ } else {
+ xml += '</hbox>';
+ }
+ xml += '</vbox>';
var bot_xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1" style="overflow: auto"><hbox>';
bot_xml += '</hbox></vbox>';
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_info_group_win" width="700" height="550" active="true"
- onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_info_win" width="700" height="550"
- onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_info_stat_cats_win" width="700" height="550"
- onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_info_surveys_win" width="700" height="550"
- onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/patron/items_overlay.xul"?>
<window id="items_win" active="true"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
</box>
<vbox id="cmvb1" flex="1">
- <groupbox id="cmgb1" flex="1">
+ <groupbox id="cmgb1" flex="1" oils_persist="height">
<caption label="&staff.patron_navbar.items;" />
<vbox flex="0">
<hbox id="items_top_ui" />
<hbox id="items_bottom_ui" />
</vbox>
</groupbox>
- <splitter><grippy/></splitter>
- <groupbox flex="1">
+ <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="cmgb1 after_splitter"><grippy/></splitter>
+ <groupbox flex="1" id="after_splitter" oils_persist="height">
<caption label="&staff.patron_navbar.items.problem_items.caption;" />
<vbox flex="0">
<hbox id="items_top_ui2" />
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="new_penalty_win"
- onload="try { new_penalty_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { new_penalty_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
+ oils_persist="height width sizemode"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&staff.patron_display.apply_penalty_dialog.title;">
<?xul-overlay href="/xul/server/patron/search_form_overlay.xul"?>
<window id="patron_search_form_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/patron/search_form_horiz_overlay.xul"?>
<window id="patron_search_form_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<?xul-overlay href="/xul/server/patron/search_result_overlay.xul"?>
<window id="patron_search_result_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<!-- CONTENT -->
- <groupbox id="penalty_groupbox" flex="1" class="my_overflow">
+ <groupbox id="penalty_groupbox" flex="1" class="my_overflow" oils_persist="height">
<caption id="penalty_caption" label="&staff.patron_display.penalty.caption;"/>
<vbox flex="0">
<hbox flex="1">
</vbox>
<tree id="ausp_list" flex="1" enableColumnDrag="true" context="ausp_actions" />
</groupbox>
- <splitter id="list_splitter" collapse="after" oils_persist="state hidden"><grippy id="splitter_grippy"/></splitter>
- <groupbox id="archived_penalty_groupbox" flex="1" class="my_overflow">
+ <splitter id="list_splitter" collapse="after" oils_persist="state hidden" oils_persist_peers="penalty_groupbox archived_penalty_groupbox"><grippy id="splitter_grippy"/></splitter>
+ <groupbox id="archived_penalty_groupbox" flex="1" class="my_overflow" oils_persist="height">
<caption id="penalty_caption" label="&staff.patron_display.archived_penalty.caption;"/>
<vbox flex="0">
<toolbox flex="1">
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="patron_summary_win"
- onload="try { font_helper(); my_init(); } catch(E) { alert(E); }" onunload="try { observer.unregister(); } catch(E) { alert(E); }"
+ onload="try { font_helper(); my_init(); persist_helper(); } catch(E) { alert(E); }" onunload="try { observer.unregister(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
}
}
-function uEditResetPw(pw) {
- if(!pw) pw = uEditMakeRandomPw(patron);
- $('ue_password1').value = pw;
- $('ue_password2').value = pw;
- $('ue_password1').onchange();
+function uEditResetPw(pw) {
+ if(!pw) {
+ if(uEditUsePhonePw) {
+ if( (pw = patron.day_phone()) ||
+ (pw = patron.evening_phone()) || (pw = patron.other_phone()) ) {
+ pw = pw.substring(pw.length - 4); // this is iffy
+ uEditResetPw(pw);
+ appendClear($('ue_password_plain'), text(pw));
+ unHideMe($('ue_password_gen'));
+ }
+ } else {
+ pw = uEditMakeRandomPw(patron);
+ }
+ $('ue_password1').value = pw;
+ $('ue_password2').value = pw;
+ $('ue_password1').onchange();
+ }
}
+
function uEditClone(clone) {
var cloneUser = fetchFleshedUser(clone);
id : 'ue_day_phone',
type : 'input',
regex : phoneRegex,
- onblur : function() {
- if(uEditUsePhonePw)
- uEditMakePhonePw();
- },
onpostchange: function(field, newval) {
/* if this is a new patron and we are using the phone number for
the password and the staff edits the phone number after entering
widget : {
id : 'ue_night_phone',
type : 'input',
- regex : phoneRegex,
- onblur : function() {
- if(uEditUsePhonePw)
- uEditMakePhonePw();
- }
+ regex : phoneRegex
}
},
{
widget : {
id : 'ue_other_phone',
type : 'input',
- regex : phoneRegex,
- onblur : function() {
- if(uEditUsePhonePw)
- uEditMakePhonePw();
- }
+ regex : phoneRegex
}
},
{
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
<window id="example_template_win"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
JSAN.use('util.window'); var win = new util.window();
win.open(
xulG.url_prefix(urls.XUL_SERIAL_SELECT_UNIT),
- 'sel_serial_sunit_win_' + win.window_name_increment(),
+ '_blank',
'chrome,resizable,modal,centerscreen'
);
if (!g.serial_items_sunit_select) {
</menu>
</menubar>
</hbox>
- <tree id="item_tree" flex="2" enableColumnDrag="true" context="serial_manage_items_popup"/>
- <splitter state="open" collapse="after" resizebefore="closest" resizeafter="farthest"/>
- <hbox align="center">
+ <tree id="item_tree" flex="2" enableColumnDrag="true" context="serial_manage_items_popup" oils_persist="height"/>
+ <splitter state="open" collapse="after" resizebefore="closest" resizeafter="farthest" id="splitter" oils_persist="state hidden" oils_persist_peers="item_tree after_splitter"/>
+ <hbox align="center" id="after_splitter" oils_persist="height">
<label style="font-weight: bold" value="Showing: "/>
<label id="serial_workarea_mode_label" value="Recently Received"/>
<spacer flex="1"/>
</popupset>
<hbox flex="1">
- <vbox flex="1">
+ <vbox flex="1" id="before_splitter" oils_persist="width">
<hbox id="serial_sub_lib_menu"/>
<hbox>
<checkbox id="show_ssubs" label="Show Subs." />
</hbox>
<tree id="subs_tree" flex="15" enableColumnDrag="true" context="serial_manage_subs_popup"/>
</vbox>
- <splitter state="open" collapse="before" resizebefore="closest" resizeafter="farthest"/>
- <deck id="serial_manage_subs_editor_deck" flex="20">
+ <splitter state="open" collapse="before" resizebefore="closest" resizeafter="farthest" id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter serial_manage_subs_editor_deck"/>
+ <deck id="serial_manage_subs_editor_deck" flex="20" oils_persist="width">
<description value="Please select an object to edit"/>
<vbox id="serial_ssub_editor_panel" />
<vbox id="serial_sdist_editor_panel" />
<!-- OVERLAYS -->
<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?>
-<window id="notes_win" width="700" height="550"
- onload="try{ my_init(); font_helper(); } catch(E) { alert(E); }"
+<window id="notes_win" width="700" height="550" oils_persist="height width sizemode"
+ onload="try{ my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<vbox id="brief_display_box"/>
<hbox flex="1" style="overflow: auto">
- <vbox flex="1">
+ <vbox flex="1" id="before_splitter" oils_persist="height">
<label value="Distribution" style="font-weight: bold; font-size: large"/>
<vbox id="sdist_editor_left_pane" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter" oils_persist="height">
<vbox id="sdist_editor_right_pane"/>
<groupbox>
<caption label="Library Specific Options" />
<?xul-overlay href="/xul/server/serial/manage_subs.xul"?>
<window id="serial_serctrl_main"
- onload="try { my_init(); font_helper(); } catch(E) { alert(E); }"
+ onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -->
<vbox id="brief_display_box"/>
<hbox flex="1" style="overflow: auto">
- <vbox flex="1">
+ <vbox flex="1" id="before_splitter1" oils_persist="width">
<label value="Issuance" style="font-weight: bold; font-size: large"/>
<vbox id="siss_editor_left_pane" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter1" oils_persist="width">
<vbox id="siss_editor_middle_pane"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_splitter1 after_splitter2"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter2" oils_persist="width">
<vbox id="siss_editor_right_pane"/>
</vbox>
</hbox>
<groupbox flex="1" class="my_overflow">
<hbox flex="1" style="overflow: auto">
- <vbox flex="1">
+ <vbox flex="1" id="before_splitter1" oils_persist="width">
<label value="Item" style="font-weight: bold; font-size: large"/>
<vbox id="sitem_editor_left_pane" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter1" oils_persist="state hidden" oils_persist_peers="before_splitter1 after_splitter1"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter1" oils_persist="width">
<label value=" " style="font-weight: bold; font-size: large"/>
<vbox id="sitem_editor_middle_pane" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter2" oils_persist="state hidden" oils_persist_peers="after_slitter1 after_splitter2"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter2" oils_persist="width">
<button style="font-weight: bold; font-size: normal" label="Item Dates" accesskey="1" oncommand="document.getElementById('sitem_editor_right_pane').firstChild.firstChild.focus();"/>
<vbox id="sitem_editor_right_pane" flex="1"/>
</vbox>
<vbox id="brief_display_box"/>
<hbox flex="1" style="overflow: auto">
- <vbox flex="1">
+ <vbox flex="1" id="before_splitter" oils_persist="width">
<label value="Subscription" style="font-weight: bold; font-size: large"/>
<vbox id="left_pane" flex="1"/>
</vbox>
- <splitter><grippy /></splitter>
- <vbox flex="1">
+ <splitter id="splitter" oils_persist="state hidden" oils_persist_peers="before_splitter after_splitter"><grippy /></splitter>
+ <vbox flex="1" id="after_splitter" oils_persist="width">
<label value=" " style="font-weight: bold; font-size: large"/>
<vbox id="right_pane" flex="1"/>
</vbox>
--- /dev/null
+/* Settings here override default values from constants.js;for example:
+
+ urls['AUDIO_good'] = '/xul/server/skin/media/custom/good.wav';
+ urls['opac'] = '/opac/' + LOCALE + '/skin/mylib/xml/advanced.xml?nps=1';
+ urls['opac_rdetail'] = '/opac/' + LOCALE + '/skin/mylib/xml/rdetail.xml';
+ urls['opac_rresult'] = '/opac/' + LOCALE + '/skin/mylib/xml/rresult.xml';
+ urls['browser'] = '/opac/' + LOCALE + '/skin/mylib/xml/advanced.xml?nps=1';
+
+*/
.PATRON_EXCEEDS_FINES label.max_bills_indicator { display: inline; color: purple; }
.PATRON_EXCEEDS_FINES label#under_bills { color: red; }
-.PATRON_AGE_LT_18 .dob { color: purple; }
-.PATRON_AGE_LT_18 label.juvenile_indicator { display: inline; color: purple; }
-
.PATRON_HAS_INVALID_DOB .dob { color: purple; }
.PATRON_HAS_INVALID_DOB label.invalid_dob_indicator { display: inline; color: purple }
and error-prone tasks associated with an upgrade for a developer.
Considerations:
- * Run as opensrf.
+ * Run as opensrf user
* opensrf needs sudo
- * Assumes opensrf has a configured (as in ./configure) both ILS and
- OpenSRF as svn or git-svn checkouts
+ * Assumes opensrf has OpenILS and OpenSRF repositories as svn or git-svn
+ checkouts and both have been configured (as in ./configure)
+
END_OF_USAGE
}
OSRF=${OPT_OSRFDIR:-$BASE/OpenSRF/trunk};
ILS=${OPT_EGDIR:-$(pwd)};
XUL="$INSTALL/var/web/xul";
+JSDIR="$INSTALL/lib/javascript"; # only used for FULL install
# ----------------------------------
# TEST and SANITY CHECK
cd $OSRF && make;
cd $ILS && make;
cd $OSRF && sudo make install;
+ if [ -d "$JSDIR" ]; then
+ echo "Copying OpenSRF javascript files into $JSDIR";
+ cp ./src/javascript/* $JSDIR;
+ fi
fi
sudo chown -R opensrf:opensrf $INSTALL
# [ $VERBOSE ] && echo RAW VERSION: $VERSION # TODO: for verbose mode
VERSION=$(echo $VERSION | sed -e 's/^ *0*//'); # This is a separate step so we can check $? above.
[ -z "$VERSION" ] && usage_die "config.upgrade_log missing ANY installed version data!";
-echo "* Last installed version -> $VERSION";
+echo "* Last installed version -> $VERSION";
if [ -d ./Open-ILS/src/sql/Pg ] ; then
cd ./Open-ILS/src/sql/Pg ;
fi
[ -d ./upgrade ] || usage_die "No ./upgrade directory found. Please run from Open-ILS/src/sql/Pg";
+MAX=$(ls upgrade/[0-9][0-9][0-9][0-9]* 2>/dev/null | tail -1 | cut -c9-12 ); # could take an optional arg to set this, if we wanted.
+echo "* Last upgrade file found -> $MAX";
+MAX=$(echo $MAX | sed -e 's/^ *0*//'); # remove leading zeroes
+
declare -a FILES;
+declare -a SKIPPED;
while true; do
VERSION=$(($VERSION + 1));
+ [ $VERSION -gt $MAX ] && break;
PREFIX=$(printf "%0.4d" $VERSION);
FILE=$(ls upgrade/$PREFIX* 2>/dev/null);
- [ ! -f "$FILE" ] && break;
- FILES[${#FILES[@]}]=$FILE; # "push" onto FILES array
- echo "* Pending $FILE";
+ if [ -f "$FILE" ] ; then
+ # Note: we only report skipped files once we find the next one.
+ # Otherwise, we'd report everything from $VERSION+1 to $MAX
+ for skip in ${SKIPPED[@]} ; do
+ echo "* WARNING: Upgrade $skip NOT FOUND. Skipping it.";
+ done
+ SKIPPED=(); # After we reported them, reset array.
+ FILES[${#FILES[@]}]=$FILE; # "push" onto FILES array
+ # echo "* Pending $FILE";
+ else
+ SKIPPED[${#SKIPPED[@]}]=$PREFIX; # "push" onto SKIPPED array
+ fi
done;
COUNT=${#FILES[@]};