Vandelay-based record matching and import for Acquisitions
authorMike Rylander <mrylander@gmail.com>
Mon, 19 Mar 2012 15:15:24 +0000 (11:15 -0400)
committerMike Rylander <mrylander@gmail.com>
Mon, 19 Mar 2012 15:16:12 +0000 (11:16 -0400)
From the Lauchpad description:

In its current form, Acquisitions record importing is inflexible and
unforgiving. The user is given practically no control over how inbound
records match against existing records nor how to deal with record
collisions. This proposal attempts to resolve this problem by leveraging
Vandelay for matching, merging, overlaying, and importing acquisitions
records.

Suggested work flow:

1. Any time an ACQ record should be loaded into the catalog or simply
   linked to an existing catalog record, the user is presented with an
   additional set of Vandelay upload options to control the matching and
   import behavior. This would include the vendor file upload UI, PO
   activation, and PO "Load Bibs and Items". The Vandelay options would
   include the full set of Match Sets, Merge Profiles, import options, etc.
   the user normally has.

2. Once options are selected and, when applicable, the MARC file is uploaded,
   the acquisitions records are added to a new type of ACQ Vandelay queue and
   processed like any other Vandelay records.

3. Vandelay will be taught to recognize ACQ records and update the lineitems
   to reflect any imported/linked catalog records.

4. Any records that fail to import will be available for manual inspection
   within the Vandelay interface an can be re-imported from there.

Signed-off-by: Mike Rylander <mrylander@gmail.com>

42 files changed:
Open-ILS/src/c-apps/oils_sql.c
Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/0683.hold_available_email_notify.sql [new file with mode: 0644]
Open-ILS/src/templates/opac/myopac/holds/edit.tt2
Open-ILS/src/templates/opac/myopac/prefs_notify.tt2
Open-ILS/src/templates/opac/myopac/prefs_settings.tt2
Open-ILS/src/templates/opac/parts/advanced/search.tt2
Open-ILS/src/templates/opac/parts/header.tt2
Open-ILS/src/templates/opac/parts/misc_util.tt2
Open-ILS/src/templates/opac/parts/org_selector.tt2
Open-ILS/src/templates/opac/parts/place_hold.tt2
Open-ILS/src/templates/opac/parts/searchbar.tt2
Open-ILS/tests/datasets/concerto.sql
Open-ILS/web/css/skin/default.css
Open-ILS/web/images/minus_sign.png
Open-ILS/web/images/plus_sign.png
Open-ILS/web/images/rdetail_arrow.png
Open-ILS/web/images/rdetail_arrow_down.png
Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
Open-ILS/xul/staff_client/chrome/content/util/list.js
Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties
Open-ILS/xul/staff_client/server/admin/patrons_due_refunds.js
Open-ILS/xul/staff_client/server/admin/transit_list.js
Open-ILS/xul/staff_client/server/cat/copy_browser.js
Open-ILS/xul/staff_client/server/cat/copy_buckets.js
Open-ILS/xul/staff_client/server/cat/manage_multi_home_items.js
Open-ILS/xul/staff_client/server/cat/z3950.js
Open-ILS/xul/staff_client/server/circ/checkin.js
Open-ILS/xul/staff_client/server/circ/copy_status.js
Open-ILS/xul/staff_client/server/patron/bill2.js
Open-ILS/xul/staff_client/server/patron/holds.js
Open-ILS/xul/staff_client/server/patron/items.js
Open-ILS/xul/staff_client/server/patron/staged.js
Open-ILS/xul/staff_client/server/patron/standing_penalties.js
Open-ILS/xul/staff_client/server/patron/trigger_events.js
Open-ILS/xul/staff_client/server/serial/manage_dists.js
Open-ILS/xul/staff_client/server/serial/manage_items.js
Open-ILS/xul/staff_client/server/serial/manage_subs.js
Open-ILS/xul/staff_client/server/skin/global.css

index 99918d5..29d80d5 100644 (file)
@@ -119,7 +119,7 @@ static ClassInfo* add_joined_class( const char* alias, const char* classname );
 static void clear_query_stack( void );
 
 static const jsonObject* verifyUserPCRUD( osrfMethodContext* );
-static int verifyObjectPCRUD( osrfMethodContext*, const jsonObject*, const int );
+static int verifyObjectPCRUD( osrfMethodContext*, osrfHash*, const jsonObject*, int );
 static const char* org_tree_root( osrfMethodContext* ctx );
 static jsonObject* single_hash( const char* key, const char* value );
 
@@ -510,7 +510,7 @@ void userDataFree( void* blob ) {
        that it will free whatever else needs freeing.
 */
 static void sessionDataFree( char* key, void* item ) {
-       if( !strcmp( key, "xact_id" ) || !strcmp( key, "authkey" ) ) 
+       if( !strcmp( key, "xact_id" ) || !strcmp( key, "authkey" ) || !strncmp( key, "rs_size_", 8) ) 
                free( item );
        else if( !strcmp( key, "user_login" ) )
                jsonObjectFree( (jsonObject*) item );
@@ -523,6 +523,20 @@ static void pcacheFree( char* key, void* item ) {
 }
 
 /**
+       @brief Initialize session cache.
+       @param ctx Pointer to the method context.
+
+       Create a cache for the session by making the session's userData member point
+       to an osrfHash instance.
+*/
+static osrfHash* initSessionCache( osrfMethodContext* ctx ) {
+       ctx->session->userData = osrfNewHash();
+       osrfHashSetCallback( (osrfHash*) ctx->session->userData, &sessionDataFree );
+       ctx->session->userDataFree = &userDataFree;
+       return ctx->session->userData;
+}
+
+/**
        @brief Save a transaction id.
        @param ctx Pointer to the method context.
 
@@ -536,11 +550,8 @@ static void setXactId( osrfMethodContext* ctx ) {
 
                // If the session doesn't already have a hash, create one.  Make sure
                // that the application session frees the hash when it terminates.
-               if( NULL == cache ) {
-                       session->userData = cache = osrfNewHash();
-                       osrfHashSetCallback( cache, &sessionDataFree );
-                       ctx->session->userDataFree = &userDataFree;
-               }
+               if( NULL == cache )
+                       cache = initSessionCache( ctx );
 
                // Save the transaction id in the hash, with the key "xact_id"
                osrfHashSet( cache, strdup( session->session_id ), "xact_id" );
@@ -586,11 +597,8 @@ static void setPermLocationCache( osrfMethodContext* ctx, const char* perm, osrf
 
                // If the session doesn't already have a hash, create one.  Make sure
                // that the application session frees the hash when it terminates.
-               if( NULL == cache ) {
-                       session->userData = cache = osrfNewHash();
-                       osrfHashSetCallback( cache, &sessionDataFree );
-                       ctx->session->userDataFree = &userDataFree;
-               }
+               if( NULL == cache )
+                       cache = initSessionCache( ctx );
 
                osrfHash* pcache = osrfHashGet(cache, "pcache");
 
@@ -641,11 +649,8 @@ static void setUserLogin( osrfMethodContext* ctx, jsonObject* user_login ) {
 
                // If the session doesn't already have a hash, create one.  Make sure
                // that the application session frees the hash when it terminates.
-               if( NULL == cache ) {
-                       session->userData = cache = osrfNewHash();
-                       osrfHashSetCallback( cache, &sessionDataFree );
-                       ctx->session->userDataFree = &userDataFree;
-               }
+               if( NULL == cache )
+                       cache = initSessionCache( ctx );
 
                if( user_login )
                        osrfHashSet( cache, user_login, "user_login" );
@@ -683,11 +688,8 @@ static void setAuthkey( osrfMethodContext* ctx, const char* authkey ) {
 
                // If the session doesn't already have a hash, create one.  Make sure
                // that the application session frees the hash when it terminates.
-               if( NULL == cache ) {
-                       session->userData = cache = osrfNewHash();
-                       osrfHashSetCallback( cache, &sessionDataFree );
-                       ctx->session->userDataFree = &userDataFree;
-               }
+               if( NULL == cache )
+                       cache = initSessionCache( ctx );
 
                // Save the transaction id in the hash, with the key "xact_id"
                if( authkey && *authkey )
@@ -763,6 +765,10 @@ static int reset_timeout( const char* authkey, time_t now ) {
 static const char* getAuthkey( osrfMethodContext* ctx ) {
        if( ctx && ctx->session && ctx->session->userData ) {
                const char* authkey = osrfHashGet( (osrfHash*) ctx->session->userData, "authkey" );
+        // LFW recent changes mean the userData hash gets set up earlier, but
+        // doesn't necessarily have an authkey yet
+        if (!authkey)
+            return NULL;
 
                // Possibly reset the authentication timeout to keep the login alive.  We do so
                // no more than once per method call, and not at all if it has been only a short
@@ -1200,14 +1206,17 @@ int doSearch( osrfMethodContext* ctx ) {
                return -1;
        }
 
-       // Return each row to the client (except that some may be suppressed by PCRUD)
-       jsonObject* cur = 0;
-       unsigned long res_idx = 0;
-       while((cur = jsonObjectGetIndex( obj, res_idx++ ) )) {
-               if( enforce_pcrud && !verifyObjectPCRUD( ctx, cur, obj->size ))
-                       continue;
-               osrfAppRespond( ctx, cur );
-       }
+       // doFieldmapperSearch() now takes care of our responding for us
+//     // Return each row to the client
+//     jsonObject* cur = 0;
+//     unsigned long res_idx = 0;
+//
+//     while((cur = jsonObjectGetIndex( obj, res_idx++ ) )) {
+//             // We used to discard based on perms here, but now that's
+//             // inside doFieldmapperSearch()
+//             osrfAppRespond( ctx, cur );
+//     }
+
        jsonObjectFree( obj );
 
        osrfAppRespondComplete( ctx, NULL );
@@ -1301,11 +1310,10 @@ int doIdList( osrfMethodContext* ctx ) {
        jsonObject* cur;
        unsigned long res_idx = 0;
        while((cur = jsonObjectGetIndex( obj, res_idx++ ) )) {
-               if( enforce_pcrud && !verifyObjectPCRUD( ctx, cur, obj->size ))
-                       continue;        // Suppress due to lack of permission
-               else
-                       osrfAppRespond( ctx,
-                               oilsFMGetObject( cur, osrfHashGet( class_meta, "primarykey" ) ) );
+               // We used to discard based on perms here, but now that's
+               // inside doFieldmapperSearch()
+               osrfAppRespond( ctx,
+                       oilsFMGetObject( cur, osrfHashGet( class_meta, "primarykey" ) ) );
        }
 
        jsonObjectFree( obj );
@@ -1350,7 +1358,7 @@ static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param )
        }
 
        if( enforce_pcrud )
-               return verifyObjectPCRUD( ctx, param, 1 );
+               return verifyObjectPCRUD( ctx, class, param, 1 );
        else
                return 1;
 }
@@ -1417,20 +1425,28 @@ static const jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
 /**
        @brief For PCRUD: Determine whether the current user may access the current row.
        @param ctx Pointer to the method context.
+       @param class Same as ctx->method->userData's item for key "class" except when called in recursive doFieldmapperSearch
        @param obj Pointer to the row being potentially accessed.
        @return 1 if access is permitted, or 0 if it isn't.
 
        The @a obj parameter points to a JSON_HASH of column values, keyed on column name.
 */
-static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, const int rs_size ) {
+static int verifyObjectPCRUD ( osrfMethodContext* ctx, osrfHash *class, const jsonObject* obj, int rs_size ) {
 
        dbhandle = writehandle;
 
        // Figure out what class and method are involved
        osrfHash* method_metadata = (osrfHash*) ctx->method->userData;
-       osrfHash* class = osrfHashGet( method_metadata, "class" );
        const char* method_type = osrfHashGet( method_metadata, "methodtype" );
 
+       if (!rs_size) {
+               int *rs_size_from_hash = osrfHashGetFmt( (osrfHash *) ctx->session->userData, "rs_size_req_%d", ctx->request );
+               if (rs_size_from_hash) {
+                       rs_size = *rs_size_from_hash;
+                       osrfLogDebug(OSRF_LOG_MARK, "used rs_size from request-scoped hash: %d", rs_size);
+               }
+       }
+
        // Set fetch to 1 in all cases except for inserts, meaning that for local or foreign
        // contexts we will do another lookup of the current row, even if we already have a
        // previously fetched row image, because the row image in hand may not include the
@@ -1480,7 +1496,11 @@ static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, c
        // Get a list of permissions from the permacrud entry.
        osrfStringArray* permission = osrfHashGet( pcrud, "permission" );
        if( permission->size == 0 ) {
-               osrfLogDebug( OSRF_LOG_MARK, "No permissions required for this action, passing through" );
+               osrfLogDebug(
+                       OSRF_LOG_MARK,
+                       "No permissions required for this action (class %s), passing through",
+                       osrfHashGet(class, "classname")
+               );
                return 1;
        }
 
@@ -1509,8 +1529,12 @@ static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, c
                osrfLogDebug( OSRF_LOG_MARK,
                                "global-level permissions required, fetching top of the org tree" );
 
+               // no need to check perms for org tree root retrieval
+               osrfHashSet((osrfHash*) ctx->session->userData, "1", "inside_verify");
                // check for perm at top of org tree
                const char* org_tree_root_id = org_tree_root( ctx );
+               osrfHashSet((osrfHash*) ctx->session->userData, "0", "inside_verify");
+
                if( org_tree_root_id ) {
                        osrfStringArrayAdd( context_org_array, org_tree_root_id );
                        osrfLogDebug( OSRF_LOG_MARK, "top of the org tree is %s", org_tree_root_id );
@@ -1555,9 +1579,11 @@ static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, c
 
                if( fetch ) {
                        // Fetch the row so that we can look at the foreign key(s)
+                       osrfHashSet((osrfHash*) ctx->session->userData, "1", "inside_verify");
                        jsonObject* _tmp_params = single_hash( pkey, pkey_value );
                        jsonObject* _list = doFieldmapperSearch( ctx, class, _tmp_params, NULL, &err );
                        jsonObjectFree( _tmp_params );
+                       osrfHashSet((osrfHash*) ctx->session->userData, "0", "inside_verify");
 
                        param = jsonObjectExtractIndex( _list, 0 );
                        jsonObjectFree( _list );
@@ -1647,8 +1673,11 @@ static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, c
 
                                        // Look up the row to which the foreign key points
                                        jsonObject* _tmp_params = single_hash( foreign_pkey, foreign_pkey_value );
+
+                                       osrfHashSet((osrfHash*) ctx->session->userData, "1", "inside_verify");
                                        jsonObject* _list = doFieldmapperSearch(
                                                ctx, osrfHashGet( oilsIDL(), class_name ), _tmp_params, NULL, &err );
+                                       osrfHashSet((osrfHash*) ctx->session->userData, "0", "inside_verify");
 
                                        jsonObject* _fparam = NULL;
                                        if( _list && JSON_ARRAY == _list->type && _list->size > 0 )
@@ -2359,7 +2388,7 @@ int doRetrieve( osrfMethodContext* ctx ) {
 
        if( enforce_pcrud ) {
                // no result, skip this entirely
-               if(NULL != obj && !verifyObjectPCRUD( ctx, obj, 1 )) {
+               if(NULL != obj && !verifyObjectPCRUD( ctx, class_def, obj, 1 )) {
                        jsonObjectFree( obj );
 
                        growing_buffer* msg = buffer_init( 128 );
@@ -2376,7 +2405,10 @@ int doRetrieve( osrfMethodContext* ctx ) {
                }
        }
 
-       osrfAppRespondComplete( ctx, obj );
+       // doFieldmapperSearch() now does the responding for us
+       //osrfAppRespondComplete( ctx, obj );
+       osrfAppRespondComplete( ctx, NULL );
+
        jsonObjectFree( obj );
        return 0;
 }
@@ -5529,9 +5561,36 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
        dbhandle = writehandle;
 
        char* core_class = osrfHashGet( class_meta, "classname" );
+       osrfLogDebug( OSRF_LOG_MARK, "entering doFieldmapperSearch() with core_class %s", core_class );
+
        char* pkey = osrfHashGet( class_meta, "primarykey" );
 
-       const jsonObject* _tmp;
+       if (!ctx->session->userData)
+               (void) initSessionCache( ctx );
+
+       char *methodtype = osrfHashGet( (osrfHash *) ctx->method->userData, "methodtype" );
+       char *inside_verify = osrfHashGet( (osrfHash*) ctx->session->userData, "inside_verify" );
+       int need_to_verify = (inside_verify ? !atoi(inside_verify) : 1);
+       int has_controller = osrfStringArrayContains(osrfHashGet(class_meta, "controller"), modulename);
+
+       int i_respond_directly = 0;
+       int flesh_depth = 0;
+
+       // XXX This can be redundant with another instance of the same test that happens
+       // within the functions that call doFieldmapperSearch(), but we have it here to
+       // prevent any non-pcrud-controlled classes from being fleshed on.
+       //
+       // TODO To avoid redundancy, move this block to right before we recurse,
+       // and change the class we're checking to the one we're /about/ to search for,
+       // not the one we're currently searching for.
+       if (
+               (!has_controller && !enforce_pcrud) // cstore client-level case: we require the controller, period
+               || (!has_controller && enforce_pcrud && need_to_verify) // pcrud case: we require the controller in need_to_verify mode
+       ) {
+               osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
+                       modulename, core_class);
+               return jsonNewObjectType( JSON_ARRAY ); /* empty */
+       }
 
        char* sql = buildSELECT( where_hash, query_hash, class_meta, ctx );
        if( !sql ) {
@@ -5569,6 +5628,30 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
        jsonObject* res_list = jsonNewObjectType( JSON_ARRAY );
        jsonObject* row_obj = NULL;
 
+       // The following two steps are for verifyObjectPCRUD()'s benefit.
+       // 1. get the flesh depth
+       const jsonObject* _tmp = jsonObjectGetKeyConst( query_hash, "flesh" );
+       if( _tmp ) {
+               flesh_depth = (int) jsonObjectGetNumber( _tmp );
+               if( flesh_depth == -1 || flesh_depth > max_flesh_depth )
+                       flesh_depth = max_flesh_depth;
+       }
+
+       // 2. figure out one consistent rs_size for verifyObjectPCRUD to use
+       // over the whole life of this request.  This means if we've already set
+       // up a rs_size_req_%d, do nothing.
+       //      a. Incidentally, we can also use this opportunity to set i_respond_directly
+       int *rs_size = osrfHashGetFmt( (osrfHash *) ctx->session->userData, "rs_size_req_%d", ctx->request );
+       if( !rs_size ) {        // pointer null, so value not set in hash
+               // i_respond_directly can only be true at the /top/ of a recursive search, if even that.
+               i_respond_directly = ( *methodtype == 'r' || *methodtype == 'i' || *methodtype == 's' );
+
+               rs_size = (int *) safe_malloc( sizeof(int) );   // will be freed by sessionDataFree()
+               unsigned long long result_count = dbi_result_get_numrows( result );
+               *rs_size = (int) result_count * (flesh_depth + 1);      // yes, we could lose some bits, but come on
+               osrfHashSet( (osrfHash *) ctx->session->userData, rs_size, "rs_size_req_%d", ctx->request );
+       }
+
        if( dbi_result_first_row( result )) {
 
                // Convert each row to a JSON_ARRAY of column values, and enclose those objects
@@ -5583,8 +5666,11 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
                                jsonObjectFree( row_obj );
                                free( pkey_val );
                        } else {
-                               osrfHashSet( dedup, pkey_val, pkey_val );
-                               jsonObjectPush( res_list, row_obj );
+                               if( !enforce_pcrud || !need_to_verify ||
+                                               verifyObjectPCRUD( ctx, class_meta, row_obj, 0 /* means check user data for rs_size */ )) {
+                                       osrfHashSet( dedup, pkey_val, pkey_val );
+                                       jsonObjectPush( res_list, row_obj );
+                               }
                        }
                } while( dbi_result_next_row( result ));
                osrfHashFree( dedup );
@@ -5599,25 +5685,24 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
        free( sql );
 
        // If we're asked to flesh, and there's anything to flesh, then flesh it
-       // (but not for PCRUD, lest the user to bypass permissions by fleshing
-       // something that he has no permission to look at).
-       if( res_list->size && query_hash && ! enforce_pcrud ) {
-               _tmp = jsonObjectGetKeyConst( query_hash, "flesh" );
-               if( _tmp ) {
-                       // Get the flesh depth
-                       int flesh_depth = (int) jsonObjectGetNumber( _tmp );
-                       if( flesh_depth == -1 || flesh_depth > max_flesh_depth )
-                               flesh_depth = max_flesh_depth;
-
-                       // We need a non-zero flesh depth, and a list of fields to flesh
-                       const jsonObject* temp_blob = jsonObjectGetKeyConst( query_hash, "flesh_fields" );
+       // (formerly we would skip fleshing if in pcrud mode, but now we support
+       // fleshing even in PCRUD).
+       if( res_list->size ) {
+               jsonObject* temp_blob;  // We need a non-zero flesh depth, and a list of fields to flesh
+               jsonObject* flesh_fields; 
+               jsonObject* flesh_blob = NULL;
+               osrfStringArray* link_fields = NULL;
+               osrfHash* links = NULL;
+               int want_flesh = 0;
+
+               if( query_hash ) {
+                       temp_blob = jsonObjectGetKey( query_hash, "flesh_fields" );
                        if( temp_blob && flesh_depth > 0 ) {
 
-                               jsonObject* flesh_blob = jsonObjectClone( temp_blob );
-                               const jsonObject* flesh_fields = jsonObjectGetKeyConst( flesh_blob, core_class );
+                               flesh_blob = jsonObjectClone( temp_blob );
+                               flesh_fields = jsonObjectGetKey( flesh_blob, core_class );
 
-                               osrfStringArray* link_fields = NULL;
-                               osrfHash* links = osrfHashGet( class_meta, "links" );
+                               links = osrfHashGet( class_meta, "links" );
 
                                // Make an osrfStringArray of the names of fields to be fleshed
                                if( flesh_fields ) {
@@ -5638,170 +5723,177 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
                                                jsonIteratorFree( _i );
                                        }
                                }
+                               want_flesh = link_fields ? 1 : 0;
+                       }
+               }
 
-                               osrfHash* fields = osrfHashGet( class_meta, "fields" );
+               osrfHash* fields = osrfHashGet( class_meta, "fields" );
 
-                               // Iterate over the JSON_ARRAY of rows
-                               jsonObject* cur;
-                               unsigned long res_idx = 0;
-                               while((cur = jsonObjectGetIndex( res_list, res_idx++ ) )) {
+               // Iterate over the JSON_ARRAY of rows
+               jsonObject* cur;
+               unsigned long res_idx = 0;
+               while((cur = jsonObjectGetIndex( res_list, res_idx++ ) )) {
 
-                                       int i = 0;
-                                       const char* link_field;
+                       int i = 0;
+                       const char* link_field;
 
-                                       // Iterate over the list of fleshable fields
-                                       while( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
+                       // Iterate over the list of fleshable fields
+                       if ( want_flesh ) {
+                               while( (link_field = osrfStringArrayGetString(link_fields, i++)) ) {
 
-                                               osrfLogDebug( OSRF_LOG_MARK, "Starting to flesh %s", link_field );
+                                       osrfLogDebug( OSRF_LOG_MARK, "Starting to flesh %s", link_field );
 
-                                               osrfHash* kid_link = osrfHashGet( links, link_field );
-                                               if( !kid_link )
-                                                       continue;     // Not a link field; skip it
+                                       osrfHash* kid_link = osrfHashGet( links, link_field );
+                                       if( !kid_link )
+                                               continue;     // Not a link field; skip it
 
-                                               osrfHash* field = osrfHashGet( fields, link_field );
-                                               if( !field )
-                                                       continue;     // Not a field at all; skip it (IDL is ill-formed)
+                                       osrfHash* field = osrfHashGet( fields, link_field );
+                                       if( !field )
+                                               continue;     // Not a field at all; skip it (IDL is ill-formed)
 
-                                               osrfHash* kid_idl = osrfHashGet( oilsIDL(),
-                                                       osrfHashGet( kid_link, "class" ));
-                                               if( !kid_idl )
-                                                       continue;   // The class it links to doesn't exist; skip it
+                                       osrfHash* kid_idl = osrfHashGet( oilsIDL(),
+                                               osrfHashGet( kid_link, "class" ));
+                                       if( !kid_idl )
+                                               continue;   // The class it links to doesn't exist; skip it
 
-                                               const char* reltype = osrfHashGet( kid_link, "reltype" );
-                                               if( !reltype )
-                                                       continue;   // No reltype; skip it (IDL is ill-formed)
+                                       const char* reltype = osrfHashGet( kid_link, "reltype" );
+                                       if( !reltype )
+                                               continue;   // No reltype; skip it (IDL is ill-formed)
 
-                                               osrfHash* value_field = field;
+                                       osrfHash* value_field = field;
 
-                                               if(    !strcmp( reltype, "has_many" )
-                                                       || !strcmp( reltype, "might_have" ) ) { // has_many or might_have
-                                                       value_field = osrfHashGet(
-                                                               fields, osrfHashGet( class_meta, "primarykey" ) );
-                                               }
-
-                                               osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
+                                       if(    !strcmp( reltype, "has_many" )
+                                               || !strcmp( reltype, "might_have" ) ) { // has_many or might_have
+                                               value_field = osrfHashGet(
+                                                       fields, osrfHashGet( class_meta, "primarykey" ) );
+                                       }
 
-                                               if( link_map->size > 0 ) {
-                                                       jsonObject* _kid_key = jsonNewObjectType( JSON_ARRAY );
-                                                       jsonObjectPush(
-                                                               _kid_key,
-                                                               jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
-                                                       );
+                                       osrfStringArray* link_map = osrfHashGet( kid_link, "map" );
 
-                                                       jsonObjectSetKey(
-                                                               flesh_blob,
-                                                               osrfHashGet( kid_link, "class" ),
-                                                               _kid_key
-                                                       );
-                                               };
+                                       if( link_map->size > 0 ) {
+                                               jsonObject* _kid_key = jsonNewObjectType( JSON_ARRAY );
+                                               jsonObjectPush(
+                                                       _kid_key,
+                                                       jsonNewObject( osrfStringArrayGetString( link_map, 0 ) )
+                                               );
 
-                                               osrfLogDebug(
-                                                       OSRF_LOG_MARK,
-                                                       "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
-                                                       osrfHashGet( kid_link, "field" ),
+                                               jsonObjectSetKey(
+                                                       flesh_blob,
                                                        osrfHashGet( kid_link, "class" ),
-                                                       osrfHashGet( kid_link, "key" ),
-                                                       osrfHashGet( kid_link, "reltype" )
+                                                       _kid_key
                                                );
+                                       };
 
-                                               const char* search_key = jsonObjectGetString(
-                                                       jsonObjectGetIndex( cur,
-                                                               atoi( osrfHashGet( value_field, "array_position" ) )
-                                                       )
-                                               );
+                                       osrfLogDebug(
+                                               OSRF_LOG_MARK,
+                                               "Link field: %s, remote class: %s, fkey: %s, reltype: %s",
+                                               osrfHashGet( kid_link, "field" ),
+                                               osrfHashGet( kid_link, "class" ),
+                                               osrfHashGet( kid_link, "key" ),
+                                               osrfHashGet( kid_link, "reltype" )
+                                       );
 
-                                               if( !search_key ) {
-                                                       osrfLogDebug( OSRF_LOG_MARK, "Nothing to search for!" );
-                                                       continue;
-                                               }
+                                       const char* search_key = jsonObjectGetString(
+                                               jsonObjectGetIndex( cur,
+                                                       atoi( osrfHashGet( value_field, "array_position" ) )
+                                               )
+                                       );
 
-                                               osrfLogDebug( OSRF_LOG_MARK, "Creating param objects..." );
+                                       if( !search_key ) {
+                                               osrfLogDebug( OSRF_LOG_MARK, "Nothing to search for!" );
+                                               continue;
+                                       }
 
-                                               // construct WHERE clause
-                                               jsonObject* where_clause  = jsonNewObjectType( JSON_HASH );
-                                               jsonObjectSetKey(
-                                                       where_clause,
-                                                       osrfHashGet( kid_link, "key" ),
-                                                       jsonNewObject( search_key )
-                                               );
+                                       osrfLogDebug( OSRF_LOG_MARK, "Creating param objects..." );
 
-                                               // construct the rest of the query, mostly
-                                               // by copying pieces of the previous level of query
-                                               jsonObject* rest_of_query = jsonNewObjectType( JSON_HASH );
-                                               jsonObjectSetKey( rest_of_query, "flesh",
-                                                       jsonNewNumberObject( flesh_depth - 1 + link_map->size )
+                                       // construct WHERE clause
+                                       jsonObject* where_clause  = jsonNewObjectType( JSON_HASH );
+                                       jsonObjectSetKey(
+                                               where_clause,
+                                               osrfHashGet( kid_link, "key" ),
+                                               jsonNewObject( search_key )
+                                       );
+
+                                       // construct the rest of the query, mostly
+                                       // by copying pieces of the previous level of query
+                                       jsonObject* rest_of_query = jsonNewObjectType( JSON_HASH );
+                                       jsonObjectSetKey( rest_of_query, "flesh",
+                                               jsonNewNumberObject( flesh_depth - 1 + link_map->size )
+                                       );
+
+                                       if( flesh_blob )
+                                               jsonObjectSetKey( rest_of_query, "flesh_fields",
+                                                       jsonObjectClone( flesh_blob ));
+
+                                       if( jsonObjectGetKeyConst( query_hash, "order_by" )) {
+                                               jsonObjectSetKey( rest_of_query, "order_by",
+                                                       jsonObjectClone( jsonObjectGetKeyConst( query_hash, "order_by" ))
                                                );
+                                       }
 
-                                               if( flesh_blob )
-                                                       jsonObjectSetKey( rest_of_query, "flesh_fields",
-                                                               jsonObjectClone( flesh_blob ));
+                                       if( jsonObjectGetKeyConst( query_hash, "select" )) {
+                                               jsonObjectSetKey( rest_of_query, "select",
+                                                       jsonObjectClone( jsonObjectGetKeyConst( query_hash, "select" ))
+                                               );
+                                       }
 
-                                               if( jsonObjectGetKeyConst( query_hash, "order_by" )) {
-                                                       jsonObjectSetKey( rest_of_query, "order_by",
-                                                               jsonObjectClone( jsonObjectGetKeyConst( query_hash, "order_by" ))
-                                                       );
-                                               }
+                                       // do the query, recursively, to expand the fleshable field
+                                       jsonObject* kids = doFieldmapperSearch( ctx, kid_idl,
+                                               where_clause, rest_of_query, err );
 
-                                               if( jsonObjectGetKeyConst( query_hash, "select" )) {
-                                                       jsonObjectSetKey( rest_of_query, "select",
-                                                               jsonObjectClone( jsonObjectGetKeyConst( query_hash, "select" ))
-                                                       );
-                                               }
+                                       jsonObjectFree( where_clause );
+                                       jsonObjectFree( rest_of_query );
 
-                                               // do the query, recursively, to expand the fleshable field
-                                               jsonObject* kids = doFieldmapperSearch( ctx, kid_idl,
-                                                       where_clause, rest_of_query, err );
+                                       if( *err ) {
+                                               osrfStringArrayFree( link_fields );
+                                               jsonObjectFree( res_list );
+                                               jsonObjectFree( flesh_blob );
+                                               return NULL;
+                                       }
 
-                                               jsonObjectFree( where_clause );
-                                               jsonObjectFree( rest_of_query );
+                                       osrfLogDebug( OSRF_LOG_MARK, "Search for %s return %d linked objects",
+                                               osrfHashGet( kid_link, "class" ), kids->size );
 
-                                               if( *err ) {
-                                                       osrfStringArrayFree( link_fields );
-                                                       jsonObjectFree( res_list );
-                                                       jsonObjectFree( flesh_blob );
-                                                       return NULL;
-                                               }
+                                       // Traverse the result set
+                                       jsonObject* X = NULL;
+                                       if( link_map->size > 0 && kids->size > 0 ) {
+                                               X = kids;
+                                               kids = jsonNewObjectType( JSON_ARRAY );
 
-                                               osrfLogDebug( OSRF_LOG_MARK, "Search for %s return %d linked objects",
-                                                       osrfHashGet( kid_link, "class" ), kids->size );
-
-                                               // Traverse the result set
-                                               jsonObject* X = NULL;
-                                               if( link_map->size > 0 && kids->size > 0 ) {
-                                                       X = kids;
-                                                       kids = jsonNewObjectType( JSON_ARRAY );
-
-                                                       jsonObject* _k_node;
-                                                       unsigned long res_idx = 0;
-                                                       while((_k_node = jsonObjectGetIndex( X, res_idx++ ) )) {
-                                                               jsonObjectPush(
-                                                                       kids,
-                                                                       jsonObjectClone(
-                                                                               jsonObjectGetIndex(
-                                                                                       _k_node,
-                                                                                       (unsigned long) atoi(
+                                               jsonObject* _k_node;
+                                               unsigned long res_idx = 0;
+                                               while((_k_node = jsonObjectGetIndex( X, res_idx++ ) )) {
+                                                       jsonObjectPush(
+                                                               kids,
+                                                               jsonObjectClone(
+                                                                       jsonObjectGetIndex(
+                                                                               _k_node,
+                                                                               (unsigned long) atoi(
+                                                                                       osrfHashGet(
                                                                                                osrfHashGet(
                                                                                                        osrfHashGet(
                                                                                                                osrfHashGet(
-                                                                                                                       osrfHashGet(
-                                                                                                                               oilsIDL(),
-                                                                                                                               osrfHashGet( kid_link, "class" )
-                                                                                                                       ),
-                                                                                                                       "fields"
+                                                                                                                       oilsIDL(),
+                                                                                                                       osrfHashGet( kid_link, "class" )
                                                                                                                ),
-                                                                                                               osrfStringArrayGetString( link_map, 0 )
+                                                                                                               "fields"
                                                                                                        ),
-                                                                                                       "array_position"
-                                                                                               )
+                                                                                                       osrfStringArrayGetString( link_map, 0 )
+                                                                                               ),
+                                                                                               "array_position"
                                                                                        )
                                                                                )
                                                                        )
-                                                               );
-                                                       } // end while loop traversing X
-                                               }
+                                                               )
+                                                       );
+                                               } // end while loop traversing X
+                                       }
+
+                                       if (kids->size > 0) {
 
-                                               if(    !strcmp( osrfHashGet( kid_link, "reltype" ), "has_a" )
-                                                       || !strcmp( osrfHashGet( kid_link, "reltype" ), "might_have" )) {
+                                               if((   !strcmp( osrfHashGet( kid_link, "reltype" ), "has_a" )
+                                                       || !strcmp( osrfHashGet( kid_link, "reltype" ), "might_have" ))
+                                               ) {
                                                        osrfLogDebug(OSRF_LOG_MARK, "Storing fleshed objects in %s",
                                                                osrfHashGet( kid_link, "field" ));
                                                        jsonObjectSetIndex(
@@ -5810,38 +5902,52 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
                                                                jsonObjectClone( jsonObjectGetIndex( kids, 0 ))
                                                        );
                                                }
+                                       }
 
-                                               if( !strcmp( osrfHashGet( kid_link, "reltype" ), "has_many" )) {
-                                                       // has_many
-                                                       osrfLogDebug( OSRF_LOG_MARK, "Storing fleshed objects in %s",
-                                                               osrfHashGet( kid_link, "field" ) );
-                                                       jsonObjectSetIndex(
-                                                               cur,
-                                                               (unsigned long) atoi( osrfHashGet( field, "array_position" ) ),
-                                                               jsonObjectClone( kids )
-                                                       );
-                                               }
-
-                                               if( X ) {
-                                                       jsonObjectFree( kids );
-                                                       kids = X;
-                                               }
+                                       if( !strcmp( osrfHashGet( kid_link, "reltype" ), "has_many" )) {
+                                               // has_many
+                                               osrfLogDebug( OSRF_LOG_MARK, "Storing fleshed objects in %s",
+                                                       osrfHashGet( kid_link, "field" ) );
+                                               jsonObjectSetIndex(
+                                                       cur,
+                                                       (unsigned long) atoi( osrfHashGet( field, "array_position" ) ),
+                                                       jsonObjectClone( kids )
+                                               );
+                                       }
 
+                                       if( X ) {
                                                jsonObjectFree( kids );
+                                               kids = X;
+                                       }
 
-                                               osrfLogDebug( OSRF_LOG_MARK, "Fleshing of %s complete",
-                                                       osrfHashGet( kid_link, "field" ) );
-                                               osrfLogDebug( OSRF_LOG_MARK, "%s", jsonObjectToJSON( cur ));
+                                       jsonObjectFree( kids );
+
+                                       osrfLogDebug( OSRF_LOG_MARK, "Fleshing of %s complete",
+                                               osrfHashGet( kid_link, "field" ) );
+                                       osrfLogDebug( OSRF_LOG_MARK, "%s", jsonObjectToJSON( cur ));
 
-                                       } // end while loop traversing list of fleshable fields
-                               } // end while loop traversing res_list
-                               jsonObjectFree( flesh_blob );
-                               osrfStringArrayFree( link_fields );
+                               } // end while loop traversing list of fleshable fields
                        }
-               }
+
+                       if( i_respond_directly ) {
+                               if ( *methodtype == 'i' ) {
+                                       osrfAppRespond( ctx,
+                                               oilsFMGetObject( cur, osrfHashGet( class_meta, "primarykey" ) ) );
+                               } else {
+                                       osrfAppRespond( ctx, cur );
+                               }
+                       }
+               } // end while loop traversing res_list
+               jsonObjectFree( flesh_blob );
+               osrfStringArrayFree( link_fields );
        }
 
-       return res_list;
+       if( i_respond_directly ) {
+               jsonObjectFree( res_list );
+               return jsonNewObjectType( JSON_ARRAY );
+       } else {
+               return res_list;
+       }
 }
 
 
@@ -6136,7 +6242,7 @@ int doDelete( osrfMethodContext* ctx ) {
 
                id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
        } else {
-               if( enforce_pcrud && !verifyObjectPCRUD( ctx, NULL, 1 )) {
+               if( enforce_pcrud && !verifyObjectPCRUD( ctx, meta, NULL, 1 )) {
                        osrfAppRespondComplete( ctx, NULL );
                        return -1;
                }
index 8a754f0..8baf9ea 100644 (file)
@@ -1351,7 +1351,7 @@ sub upload_records {
         $po = create_purchase_order($mgr, 
             ordering_agency => $ordering_agency,
             provider => $provider->id,
-            state => 'on-order'
+            state => 'pending' # will be updated later if activated
         ) or return $mgr->editor->die_event;
     }
 
index 55c5742..f3a701a 100644 (file)
@@ -5,10 +5,13 @@ use DateTime::Format::ISO8601;
 use OpenSRF::Utils qw/:datetime/;
 use OpenSRF::Utils::Logger qw/:logger/;
 use OpenILS::Const qw/:const/;
+use OpenILS::Application::AppUtils;
 sub fourty_two { return 42 }
 sub NOOP_True { return 1 }
 sub NOOP_False { return 0 }
 
+my $U = 'OpenILS::Application::AppUtils';
+
 sub CircIsOpen {
     my $self = shift;
     my $env = shift;
@@ -73,6 +76,16 @@ sub HoldIsAvailable {
 
     my $hold = $env->{target};
 
+    if ($env->{params}->{check_email_notify}) {
+        return 0 unless $U->is_true($hold->email_notify);
+    }
+    if ($env->{params}->{check_sms_notify}) {
+        return 0 unless $hold->sms_notify;
+    }
+    if ($env->{params}->{check_phone_notify}) {
+        return 0 unless $hold->phone_notify;
+    }
+
     return 1 if 
         !$hold->cancel_time and
         !$hold->fulfillment_time and
@@ -106,7 +119,36 @@ sub HoldIsCancelled {
 
     my $hold = $env->{target};
 
+    if ($env->{params}->{check_email_notify}) {
+        return 0 unless $U->is_true($hold->email_notify);
+    }
+    if ($env->{params}->{check_sms_notify}) {
+        return 0 unless $hold->sms_notify;
+    }
+    if ($env->{params}->{check_phone_notify}) {
+        return 0 unless $hold->phone_notify;
+    }
+
     return ($hold->cancel_time) ? 1 : 0;
 }
 
+sub HoldNotifyCheck {
+    my $self = shift;
+    my $env = shift;
+
+    my $hold = $env->{target};
+
+    if ($env->{params}->{check_email_notify}) {
+        return 0 unless $U->is_true($hold->email_notify);
+    }
+    if ($env->{params}->{check_sms_notify}) {
+        return 0 unless $hold->sms_notify;
+    }
+    if ($env->{params}->{check_phone_notify}) {
+        return 0 unless $hold->phone_notify;
+    }
+
+    return 1;
+}
+
 1;
index 38966f2..6c5851f 100644 (file)
@@ -86,7 +86,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0682', :eg_version); -- berick/dbs/tsbere
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0683', :eg_version); -- tsbere/berick
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
index 45fead3..bc737d7 100644 (file)
@@ -6527,6 +6527,8 @@ The item(s) you requested are available for pickup from the Library.
 
 $$);
 
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (5, 'check_email_notify', 1);
 
 INSERT INTO action_trigger.hook (
         key,
@@ -6589,6 +6591,8 @@ pickup, but these holds will soon expire.
 $$
 );
 
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (7, 'check_email_notify', 1);
 
 INSERT INTO action_trigger.environment (
         event_def,
@@ -6612,6 +6616,15 @@ INSERT INTO action_trigger.hook (
         TRUE
     );
 
+INSERT INTO action_trigger.validator (module,description) VALUES
+    ('HoldNotifyCheck',
+    oils_i18n_gettext(
+        'HoldNotifyCheck',
+        'Check Hold notification flag(s)',
+        'atval',
+        'description'
+    ));
+
 INSERT INTO action_trigger.event_definition (
         id,
         active,
@@ -6630,7 +6643,7 @@ INSERT INTO action_trigger.event_definition (
         1,
         'Hold waiting for pickup for long time',
         'hold_request.long_wait',
-        'NOOP_True',
+        'HoldNotifyCheck',
         'SendEmail',
         '6 MONTHS',
         'request_time',
@@ -6663,6 +6676,9 @@ INSERT INTO action_trigger.environment (event_def, path)
     (9, 'usr'),
     (9, 'current_copy.call_number');
 
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (9, 'check_email_notify', 1);
+
 -- trigger data related to acq user requests
 
 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
@@ -9199,6 +9215,9 @@ INSERT INTO action_trigger.environment (event_def, path) VALUES
     (38, 'pickup_lib'),
     (38, 'bib_rec.bib_record.simple_record');
 
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (currval('action_trigger.event_definition_id_seq'), 'check_email_notify', 1);
+
 ----------------------------------------------------------------
 -- Seed data for queued record/item exports
 ----------------------------------------------------------------
@@ -11245,6 +11264,9 @@ INSERT INTO action_trigger.environment (
     'pickup_lib.billing_address'
 );
 
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (currval('action_trigger.event_definition_id_seq'), 'check_sms_notify', 1);
+
 INSERT INTO action_trigger.hook(
     key,
     core_type,
diff --git a/Open-ILS/src/sql/Pg/upgrade/0683.hold_available_email_notify.sql b/Open-ILS/src/sql/Pg/upgrade/0683.hold_available_email_notify.sql
new file mode 100644 (file)
index 0000000..2765f4e
--- /dev/null
@@ -0,0 +1,29 @@
+BEGIN;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0683', :eg_version);
+
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (5, 'check_email_notify', 1);
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (7, 'check_email_notify', 1);
+INSERT INTO action_trigger.event_params (event_def, param, value)
+    VALUES (9, 'check_email_notify', 1);
+INSERT INTO action_trigger.validator (module,description) VALUES
+    ('HoldNotifyCheck',
+    oils_i18n_gettext(
+        'HoldNotifyCheck',
+        'Check Hold notification flag(s)',
+        'atval',
+        'description'
+    ));
+UPDATE action_trigger.event_definition SET validator = 'HoldNotifyCheck' WHERE id = 9;
+
+-- NOT COVERED: Adding check_sms_notify to the proper trigger. It doesn't have a static id.
+
+COMMIT;
+
+--UNDO
+--UPDATE action_trigger.event_definition SET validator = 'NOOP_True' WHERE id = 9;
+--DELETE FROM action_trigger.event_params WHERE param = 'check_email_notify';
+--DELETE FROM action_trigger.validator WHERE module = 'HoldNotifyCheck';
index eb2612e..293199f 100644 (file)
@@ -47,7 +47,7 @@
                             [% l('Pickup library') %]
                         </th>
                         <td>
-                            [% PROCESS build_org_selector
+                            [% INCLUDE build_org_selector
                                 name='pickup_lib' value=ahr.pickup_lib %]
                         </td>
                     </tr>
index aeaaa41..48de46e 100644 (file)
@@ -4,14 +4,6 @@
     prefs_page = 'notify' %]
 
 <form method='POST'>
-
-    <div style="float:right;width:65px;">
-        <input type='submit' 
-            value="[% l('Save') %]"
-            alt="[% l('Save') %]"
-            class="opac-button" />
-    </div>
-
     [% setting = 'opac.hold_notify' %]
     <input name='[% setting %]' type="hidden"
         [% IF ctx.user_setting_map.$setting; %] value='[% ctx.user_setting_map.$setting | html %]' [% END %]/>
@@ -98,6 +90,7 @@
             [% END %]
         </tbody>
     </table>
+    <input type='submit' value="[% l('Save') %]" alt="[% l('Save') %]" class="opac-button" />
 </form>
 [% END %]
 
index 7cb3103..8e301d5 100644 (file)
@@ -45,7 +45,7 @@
                             IF ctx.user_setting_map.$setting;
                                 thang = ctx.user_setting_map.$setting;
                             END;
-                            PROCESS build_org_selector name=setting value=thang;
+                            INCLUDE build_org_selector name=setting value=thang;
                         %]
                     </td>
                 </tr>
index 6b62221..697f951 100644 (file)
@@ -67,7 +67,7 @@
                         <td valign='top'>
                             <strong>[% l("Search Library") %]</strong><br />
                             [% PROCESS "opac/parts/org_selector.tt2";
-                                PROCESS build_org_selector show_loc_groups=1 %]
+                                INCLUDE build_org_selector show_loc_groups=1 %]
                             <div style="position:relative;top:7px;">
                                 <input type='checkbox' name="modifier"
                                     value="available"[% CGI.param('modifier').grep('available').size ? ' checked="checked"' : '' %]
index 5db8139..d67a1d3 100644 (file)
@@ -59,7 +59,7 @@
             IF val == ''; cgi.delete(p); END;
 
             # Delete POST vars unless we asked for them
-            UNLESS CGI.url_param(p) OR params.defined(p);
+            UNLESS CGI.url_param(p).defined OR params.defined(p);
                 cgi.delete(p);
             END;
         END;
index 95c6f4a..25a8362 100644 (file)
         args.issn = xml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
         args.author = xml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
 
+        # Include subfields 'abnp' to generate a more comprehensive title display in search results
+        titresults = xml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b" or @code="n" or @code="p"]');
+        titresults_content = [];
+            FOR sub IN titresults; titresults_content.push(sub.textContent); END;
+        args.title = titresults_content.join(" ");
         # Avoid ugly trailing syntax on brief titles
-        args.title = xml.findnodes('//*[@tag="245"]/*[@code="a"]').textContent;
         args.title = args.title | replace('[:;/]$', '');
 
         # Provide correct spacing between the subfields
index 02c2448..fd9c8c0 100644 (file)
@@ -1,7 +1,10 @@
 [%
 # Org Unit Selector Widget :
-#   PROCESS build_org_selector id='selector-id' name='selector-name' 
+#   INCLUDE build_org_selector id='selector-id' name='selector-name' 
 #       value=org_id show_loc_groups=1/0 can_have_vols_only=1/0
+#
+# NOTE: DO NOT USE PROCESS
+# Use of PROCESS results in internal variables, such as value or org_unit, to "leak" out
 
 BLOCK build_org_selector;
     node_stack = [{org => org_unit || ctx.aou_tree}];
index 97ffe96..fc2f3e8 100644 (file)
@@ -68,7 +68,7 @@
         <p>
             [% l('Pickup location:') %]
             [% PROCESS "opac/parts/org_selector.tt2";
-                PROCESS build_org_selector name='pickup_lib' value=ctx.default_pickup_lib id='pickup_lib' can_have_vols_only=1 %]
+                INCLUDE build_org_selector name='pickup_lib' value=ctx.default_pickup_lib id='pickup_lib' can_have_vols_only=1 %]
         </p>
         <p>
             [% l('Notify when hold is ready for pickup?') %]
                 [% END %]
             </blockquote>
         </p>
-        <p>
-            [% |l %]If you use the Traveling Library Center (TLC) and ABC Express
-            services, please select "Outreach" to have the item delivered
-            during your scheduled visit.[% END %]
-        </p>
         <input type="submit" name="submit" value="[% l('Submit') %]" title="[% l('Submit') %]"
             alt="[% l('Submit') %]" class="opac-button" />
         &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
index d0fa8ea..fccac11 100644 (file)
@@ -28,7 +28,7 @@
                 [%- END # autosuggest enabled %] />
         </span>
         [%- INCLUDE "opac/parts/qtype_selector.tt2" id="qtype";
-            l(' in '); PROCESS build_org_selector show_loc_groups=1
+            l(' in '); INCLUDE build_org_selector show_loc_groups=1
     %]
     <span>
         <input id='search-submit-go' type="submit" value="[% l('Search') %]" alt="[% l('Search') %]" class="opac-button"
index 68cafb1..61ed550 100644 (file)
@@ -110,41 +110,76 @@ INSERT INTO marcxml_import (marc) VALUES
 
 INSERT INTO biblio.record_entry (marc, last_xact_id) SELECT marc, 'IMPORT' FROM marcxml_import;
 
--- Create call numbers for BR1
-INSERT INTO asset.call_number (record, creator, editor, owning_lib, label, label_class)
- SELECT id, 1, 1, 4, 'CONCERTO ' || id::text, 1
- FROM biblio.record_entry
- WHERE id > 0;
-
--- Create call numbers for BR2
-INSERT INTO asset.call_number (record, creator, editor, owning_lib, label, label_class)
- SELECT id, 1, 1, 5, 'CONCERTO ' || id::text, 1
- FROM biblio.record_entry
- WHERE id > 0;
-
--- Create call numbers for BR4
-INSERT INTO asset.call_number (record, creator, editor, owning_lib, label, label_class)
- SELECT id, 1, 1, 7, 'CONCERTO ' || id::text, 1
- FROM biblio.record_entry
- WHERE id > 0;
-
--- CREATE copies for BR1
-INSERT INTO asset.copy (call_number, circ_lib, creator, editor, loan_duration, fine_level, barcode)
- SELECT id, owning_lib, 1, 1, 1, 1, 'CONC40000' || id::text
- FROM asset.call_number
- WHERE record > 0 AND label LIKE 'CONCERTO %' AND owning_lib = 4;
+CREATE FUNCTION evergreen.populate_call_number (ownlib INTEGER, label TEXT)
+RETURNS void AS $$
+    INSERT INTO asset.call_number (record, creator, editor, owning_lib, label, label_class)
+        SELECT id, 1, 1, $1, $2 || id::text, 1
+        FROM biblio.record_entry
+        WHERE id > 0;
+$$ LANGUAGE SQL;
 
--- CREATE copies for BR2
-INSERT INTO asset.copy (call_number, circ_lib, creator, editor, loan_duration, fine_level, barcode)
- SELECT id, owning_lib, 1, 1, 1, 1, 'CONC50000' || id::text
- FROM asset.call_number
- WHERE record > 0 AND label LIKE 'CONCERTO %'  AND owning_lib = 5;
+CREATE FUNCTION evergreen.populate_copy (circlib INTEGER, ownlib INTEGER, barcode TEXT, label TEXT)
+RETURNS void AS $$
+    INSERT INTO asset.copy (call_number, circ_lib, creator, editor, loan_duration, fine_level, barcode)
+        SELECT id, $1, 1, 1, 1, 1, $3 || id::text
+        FROM asset.call_number
+        WHERE record > 0 AND label LIKE $4 || '%' AND owning_lib = $2;
+$$ LANGUAGE SQL;
 
--- CREATE copies for BR4
-INSERT INTO asset.copy (call_number, circ_lib, creator, editor, loan_duration, fine_level, barcode)
- SELECT id, owning_lib, 1, 1, 1, 1, 'CONC70000' || id::text
- FROM asset.call_number
- WHERE record > 0 AND label LIKE 'CONCERTO %' AND owning_lib = 7;
+-- Create call numbers
+SELECT evergreen.populate_call_number(4, 'CONCERTO '); -- BR1
+SELECT evergreen.populate_call_number(5, 'CONCERTO '); -- BR2
+SELECT evergreen.populate_call_number(6, 'CONCERTO '); -- BR3
+SELECT evergreen.populate_call_number(7, 'CONCERTO '); -- BR4
+SELECT evergreen.populate_call_number(9, 'CONCERTO '); -- BM1
+SELECT evergreen.populate_call_number(4, 'PERFORM '); -- BR1
+SELECT evergreen.populate_call_number(5, 'PERFORM '); -- BR2
+SELECT evergreen.populate_call_number(6, 'PERFORM '); -- BR3
+SELECT evergreen.populate_call_number(7, 'PERFORM '); -- BR4
+SELECT evergreen.populate_call_number(9, 'PERFORM '); -- BM1
+
+-- Create copies
+SELECT evergreen.populate_copy(4, 4, 'CONC40000', 'CONCERTO'); -- BR1
+SELECT evergreen.populate_copy(5, 5, 'CONC50000', 'CONCERTO'); -- BR2
+SELECT evergreen.populate_copy(6, 6, 'CONC60000', 'CONCERTO'); -- BR3
+SELECT evergreen.populate_copy(7, 7, 'CONC70000', 'CONCERTO'); -- BR4
+SELECT evergreen.populate_copy(9, 9, 'CONC90000', 'CONCERTO'); -- BM1
+
+SELECT evergreen.populate_copy(4, 4, 'CONC41000', 'CONCERTO'); -- BR1
+SELECT evergreen.populate_copy(5, 5, 'CONC51000', 'CONCERTO'); -- BR2
+SELECT evergreen.populate_copy(6, 6, 'CONC61000', 'CONCERTO'); -- BR3
+SELECT evergreen.populate_copy(7, 7, 'CONC71000', 'CONCERTO'); -- BR4
+SELECT evergreen.populate_copy(9, 9, 'CONC91000', 'CONCERTO'); -- BM1
+
+SELECT evergreen.populate_copy(4, 4, 'CONC42000', 'CONCERTO'); -- BR1
+SELECT evergreen.populate_copy(5, 5, 'CONC52000', 'CONCERTO'); -- BR2
+SELECT evergreen.populate_copy(6, 6, 'CONC62000', 'CONCERTO'); -- BR3
+SELECT evergreen.populate_copy(7, 7, 'CONC72000', 'CONCERTO'); -- BR4
+SELECT evergreen.populate_copy(9, 9, 'CONC92000', 'CONCERTO'); -- BM1
+
+SELECT evergreen.populate_copy(4, 4, 'CONC43000', 'CONCERTO'); -- BR1
+SELECT evergreen.populate_copy(5, 5, 'CONC53000', 'CONCERTO'); -- BR2
+SELECT evergreen.populate_copy(6, 6, 'CONC63000', 'CONCERTO'); -- BR3
+SELECT evergreen.populate_copy(7, 7, 'CONC73000', 'CONCERTO'); -- BR4
+SELECT evergreen.populate_copy(9, 9, 'CONC93000', 'CONCERTO'); -- BM1
+
+SELECT evergreen.populate_copy(4, 4, 'CONC44000', 'CONCERTO'); -- BR1
+SELECT evergreen.populate_copy(5, 5, 'CONC54000', 'CONCERTO'); -- BR2
+SELECT evergreen.populate_copy(6, 6, 'CONC64000', 'CONCERTO'); -- BR3
+SELECT evergreen.populate_copy(7, 7, 'CONC74000', 'CONCERTO'); -- BR4
+SELECT evergreen.populate_copy(9, 9, 'CONC94000', 'CONCERTO'); -- BM1
+
+SELECT evergreen.populate_copy(4, 4, 'CONC40000', 'PERFORM'); -- BR1
+SELECT evergreen.populate_copy(5, 5, 'CONC50000', 'PERFORM'); -- BR2
+SELECT evergreen.populate_copy(6, 6, 'CONC60000', 'PERFORM'); -- BR3
+SELECT evergreen.populate_copy(7, 7, 'CONC70000', 'PERFORM'); -- BR4
+SELECT evergreen.populate_copy(9, 9, 'CONC90000', 'PERFORM'); -- BM1
+
+SELECT evergreen.populate_copy(4, 4, 'CONC41000', 'PERFORM'); -- BR1
+SELECT evergreen.populate_copy(5, 5, 'CONC51000', 'PERFORM'); -- BR2
+SELECT evergreen.populate_copy(6, 6, 'CONC61000', 'PERFORM'); -- BR3
+SELECT evergreen.populate_copy(7, 7, 'CONC71000', 'PERFORM'); -- BR4
+SELECT evergreen.populate_copy(9, 9, 'CONC91000', 'PERFORM'); -- BM1
 
 -- Delete some copies, call numbers, and bib records
 DELETE FROM biblio.record_entry
@@ -234,5 +269,7 @@ INSERT INTO biblio.peer_bib_copy_map (peer_type, peer_record, target_copy)
     GROUP BY bpt.id, acn.record, ac.id;
 
 DROP TABLE marcxml_import;
+DROP FUNCTION evergreen.populate_call_number(INTEGER, TEXT);
+DROP FUNCTION evergreen.populate_copy(INTEGER, INTEGER, TEXT, TEXT);
 
 COMMIT;
index 1973b49..2098329 100644 (file)
@@ -75,6 +75,10 @@ table { border-collapse: collapse; }
 .dijitTooltipTable td {padding: 3px;} /* custom class for handling dialog tables */
 /* ----------------------------------------------------------------- */
 
+.autoGridLineNumber {
+    background-color: -moz-dialog;
+    font-weight: bold;
+}
 
 .oils-fm-edit-pane { margin: 5px; }
 .oils-fm-edit-pane td { padding: 5px; }
index 0d612b1..553efcf 100644 (file)
Binary files a/Open-ILS/web/images/minus_sign.png and b/Open-ILS/web/images/minus_sign.png differ
index a17d58a..1f12d14 100644 (file)
Binary files a/Open-ILS/web/images/plus_sign.png and b/Open-ILS/web/images/plus_sign.png differ
index e464bf2..24b0053 100644 (file)
Binary files a/Open-ILS/web/images/rdetail_arrow.png and b/Open-ILS/web/images/rdetail_arrow.png differ
index 3dd6bfd..9ccadd3 100644 (file)
Binary files a/Open-ILS/web/images/rdetail_arrow_down.png and b/Open-ILS/web/images/rdetail_arrow_down.png differ
index 921f977..0faa3ee 100644 (file)
@@ -25,7 +25,9 @@ if(!dojo._hasResource['openils.widget.AutoGrid']) {
             suppressEditFields : null,
             suppressFilterFields : null,
             hideSelector : false,
+            hideLineNumber : false,
             selectorWidth : '1.5',
+            lineNumberWidth : '1.5',
             showColumnPicker : false,
             columnPickerPrefix : null,
             displayLimit : 15,
@@ -38,6 +40,14 @@ if(!dojo._hasResource['openils.widget.AutoGrid']) {
             /* by default, don't show auto-generated (sequence) fields */
             showSequenceFields : false, 
 
+            // style the cells in the line number column
+            onStyleRow : function(row) {
+                if (!this.hideLineNumber) {
+                    var cellIdx = this.hideSelector ? 0 : 1;
+                    dojo.addClass(this.views.views[0].getCellNode(row.index, cellIdx), 'autoGridLineNumber');
+                }
+            },
+
             startup : function() {
                 this.selectionMode = 'single';
                 this.sequence = openils.widget.AutoGrid.sequence++;
@@ -156,6 +166,10 @@ if(!dojo._hasResource['openils.widget.AutoGrid']) {
             canSort : function(rowIdx) {
                 if(rowIdx == 1 && !this.hideSelector)
                     return false;
+                if(this.hideSelector && rowIdx == 1 && !this.hideLineNumber)
+                    return false;
+                if(!this.hideSelector && rowIdx == 2 && !this.hideLineNumber)
+                    return false;
                 return true;
             },
 
@@ -189,6 +203,16 @@ if(!dojo._hasResource['openils.widget.AutoGrid']) {
                     });
                 }
 
+                if(!this.hideLineNumber) {
+                    // insert the line number column
+                    pushEntry({
+                        field : '+lineno',
+                        get : function(rowIdx, item) { if(item) return 1 + rowIdx; },
+                        width : this.lineNumberWidth,
+                        name : '#',
+                        nonSelectable : false
+                    });
+                }
 
                 if(!this.fieldOrder) {
                     /* no order defined, start with any explicit grid fields */
index 605bc44..6dcc359 100644 (file)
@@ -51,7 +51,23 @@ util.list.prototype = {
         if (typeof params.prebuilt != 'undefined') obj.prebuilt = params.prebuilt;
 
         if (typeof params.columns == 'undefined') throw('util.list.init: No columns');
-        obj.columns = [];
+        obj.columns = [
+            {
+                'id' : 'lineno',
+                'label' : document.getElementById('offlineStrings').getString('list.line_number'),
+                'flex' : '0',
+                'no_sort' : 'true',
+                'properties' : 'ordinal', // column properties for css styling
+                'hidden' : 'false',
+                'editable' : false,
+                'render' : function(my,scratch) {
+                    // special code will handle this based on the attribute we set
+                    // here.  All cells for this column need to be updated whenever
+                    // a list adds, removes, or sorts rows
+                    return '_';
+                }
+            }
+        ];
         for (var i = 0; i < params.columns.length; i++) {
             if (typeof params.columns[i] == 'object') {
                 obj.columns.push( params.columns[i] );
@@ -407,14 +423,14 @@ util.list.prototype = {
         }
         if (rparams && params.attributes) {
             for (var i in params.attributes) {
-                rparams.my_node.setAttribute(i,params.attributes[i]);
+                rparams.treeitem_node.setAttribute(i,params.attributes[i]);
             }
         }
         this.row_count.total++;
         if (this.row_count.fleshed == this.row_count.total) {
             setTimeout( function() { obj.exec_on_all_fleshed(); }, 0 );
         }
-        rparams.my_node.setAttribute('unique_row_counter',obj.unique_row_counter);
+        rparams.treeitem_node.setAttribute('unique_row_counter',obj.unique_row_counter);
         rparams.unique_row_counter = obj.unique_row_counter++;
         if (typeof params.on_append == 'function') {
             params.on_append(rparams);
@@ -431,7 +447,7 @@ util.list.prototype = {
         }
         if (rparams && params.attributes) {
             for (var i in params.attributes) {
-                rparams.my_node.setAttribute(i,params.attributes[i]);
+                rparams.treeitem_node.setAttribute(i,params.attributes[i]);
             }
         }
         this.row_count.fleshed--;
@@ -524,13 +540,13 @@ util.list.prototype = {
                         }
                     }
 
-                    params.row_node = treeitem;
+                    params.treeitem_node = treeitem;
                     params.on_retrieve = function(p) {
                         try {
                             p.row = params.row;
                             obj._map_row_to_treecell(p,treerow);
                             inc_fleshed();
-                            var idx = obj.node.contentView.getIndexOfItem( params.row_node );
+                            var idx = obj.node.contentView.getIndexOfItem( params.treeitem_node );
                             dump('idx = ' + idx + '\n');
                             // if current row is selected, send another select event to re-sync data that the client code fetches on selects
                             if ( obj.node.view.selection.isSelected( idx ) ) {
@@ -611,9 +627,9 @@ util.list.prototype = {
             } catch(E) {
             }
 
-        setTimeout( function() { obj.auto_retrieve(); }, 0 );
+        setTimeout( function() { obj.auto_retrieve(); obj.refresh_ordinals(); }, 0 );
 
-        params.my_node = treeitem;
+        params.treeitem_node = treeitem;
         return params;
     },
 
@@ -622,12 +638,12 @@ util.list.prototype = {
         var obj = this;
 
         if (typeof params.row == 'undefined') throw('util.list.refresh_row: Object must contain a row');
-        if (typeof params.my_node == 'undefined') throw('util.list.refresh_row: Object must contain a my_node');
-        if (params.my_node.nodeName != 'treeitem') throw('util.list.refresh_rwo: my_node must be a treeitem');
+        if (typeof params.treeitem_node == 'undefined') throw('util.list.refresh_row: Object must contain a treeitem_node');
+        if (params.treeitem_node.nodeName != 'treeitem') throw('util.list.refresh_rwo: treeitem_node must be a treeitem');
 
         var s = ('util.list.refresh_row: params = ' + (params) + '\n');
 
-        var treeitem = params.my_node;
+        var treeitem = params.treeitem_node;
         treeitem.setAttribute('retrieve_id',params.retrieve_id);
         if (typeof params.to_bottom != 'undefined') {
             if (typeof params.no_auto_select == 'undefined') {
@@ -684,13 +700,13 @@ util.list.prototype = {
                         }
                     }
 
-                    params.row_node = treeitem;
+                    params.treeitem_node = treeitem;
                     params.on_retrieve = function(p) {
                         try {
                             p.row = params.row;
                             obj._map_row_to_treecell(p,treerow);
                             inc_fleshed();
-                            var idx = obj.node.contentView.getIndexOfItem( params.row_node );
+                            var idx = obj.node.contentView.getIndexOfItem( params.treeitem_node );
                             dump('idx = ' + idx + '\n');
                             // if current row is selected, send another select event to re-sync data that the client code fetches on selects
                             if ( obj.node.view.selection.isSelected( idx ) ) {
@@ -773,7 +789,7 @@ util.list.prototype = {
             } catch(E) {
             }
 
-        setTimeout( function() { obj.auto_retrieve(); }, 0 );
+        setTimeout( function() { obj.auto_retrieve(); obj.refresh_ordinals(); }, 0 );
 
         JSAN.use('util.widgets'); util.widgets.dispatch('select',obj.node);
 
@@ -782,6 +798,37 @@ util.list.prototype = {
         return params;
     },
 
+    'refresh_ordinals' : function() {
+        var obj = this;
+        try {
+            setTimeout( // Otherwise we can miss a row just added
+                function() {
+                    var nl = document.getElementsByAttribute('label','_');
+                    for (var i = 0; i < nl.length; i++) {
+                        nl[i].setAttribute(
+                            'ord_col',
+                            'true'
+                        );
+                        nl[i].setAttribute( // treecell properties for css styling
+                            'properties',
+                            'ordinal'
+                        );
+                    }
+                    nl = document.getElementsByAttribute('ord_col','true');
+                    for (var i = 0; i < nl.length; i++) {
+                        nl[i].setAttribute(
+                            'label',
+                            // we could just use 'i' here if we trust the order of elements
+                            1 + obj.node.contentView.getIndexOfItem(nl[i].parentNode.parentNode) // treeitem
+                        );
+                    }
+                }, 1000
+            );
+        } catch(E) {
+            alert('Error in list.js, refresh_ordinals(): ' + E);
+        }
+    },
+
     'put_retrieving_label' : function(treerow) {
         var obj = this;
         try {
@@ -967,7 +1014,7 @@ util.list.prototype = {
                     //FIXME//Make async and fire when row is visible in list
                     var row;
 
-                    params.row_node = listitem;
+                    params.treeitem_node = listitem;
                     params.on_retrieve = function(row) {
                         params.row = row;
                         obj._map_row_to_listcell(params,listitem);
@@ -995,7 +1042,7 @@ util.list.prototype = {
         }
 
         this.error.sdump('D_LIST',s);
-        params.my_node = listitem;
+        params.treeitem_node = listitem;
         return params;
 
     },
@@ -1512,7 +1559,7 @@ util.list.prototype = {
     '_sort_tree' : function(col,sortDir) {
         var obj = this;
         try {
-            if (obj.node.getAttribute('no_sort')) {
+            if (obj.node.getAttribute('no_sort') || col.getAttribute('no_sort')) {
                 return;
             }
             var col_pos;
@@ -1592,6 +1639,7 @@ util.list.prototype = {
                     } catch(E) {
                         obj.error.standard_unexpected_error_alert('sorting',E); 
                     }
+                    obj.refresh_ordinals();
                 }
             );
         } catch(E) {
index eaef490..71485fc 100644 (file)
@@ -224,6 +224,7 @@ list.actions.csv_to_file.accesskey=F
 list.actions.save_column_configuration.label=Save Column Configuration
 list.actions.save_column_configuration.accesskey=S
 list.dump_extended_format.record_separator==-=-=
+list.line_number=#
 menu.cmd_survey_wizard.inadequate_perm=You are lacking the CREATE_SURVEY permission and/or working locations.
 menu.cmd_local_admin_fonts_and_sounds.tab=Global Font and Sound Settings
 menu.cmd_local_admin_printer.tab=Printer Settings Editor
index 5a0f15f..3694f65 100644 (file)
@@ -109,7 +109,7 @@ function init_list() {
 }
 
 function retrieve_row(params) { // callback function for fleshing rows in a list
-    params.row_node.setAttribute('retrieve_id',params.row.my.au.id()); 
+    params.treeitem_node.setAttribute('retrieve_id',params.row.my.au.id()); 
     params.on_retrieve(params.row); 
     return params.row; 
 }
index 4024ec1..2d841ce 100644 (file)
@@ -339,7 +339,7 @@ admin.transit_list.prototype = {
                                                 if (typeof r_mvr.ilsevent != 'undefined') throw(r_mvr);
                                                 row.my.mvr = r_mvr;
 
-                                                params.row_node.setAttribute(
+                                                params.treeitem_node.setAttribute(
                                                     'retrieve_id', js2JSON( { 
                                                         'copy_id' : row.my.acp ? row.my.acp.id() : null, 
                                                         'doc_id' : row.my.mvr ? row.my.mvr.doc_id() : null,  
@@ -359,7 +359,7 @@ admin.transit_list.prototype = {
                                         }
                                     );
                                 } else {
-                                    params.row_node.setAttribute(
+                                    params.treeitem_node.setAttribute(
                                         'retrieve_id', js2JSON( { 
                                             'copy_id' : row.my.acp ? row.my.acp.id() : null, 
                                             'doc_id' : row.my.mvr ? row.my.mvr.doc_id() : null,  
index ce3c578..d60a119 100644 (file)
@@ -1565,7 +1565,8 @@ cat.copy_browser.prototype = {
                 data.node = obj.map_tree[ 'aou_' + parent_org.id() ];
             }
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            obj.list.refresh_ordinals();
+            var node = nparams.treeitem_node;
             if (params) {
                 for (var i in params) {
                     node.setAttribute(i,params[i]);
@@ -1631,7 +1632,8 @@ cat.copy_browser.prototype = {
                 'no_auto_select' : true,
             };
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            obj.list.refresh_ordinals();
+            var node = nparams.treeitem_node;
             obj.map_tree[ 'acn_' + acn_tree.id() ] =  node;
             if (params) {
                 for (var i in params) {
@@ -1692,7 +1694,8 @@ cat.copy_browser.prototype = {
                 'no_auto_select' : true,
             };
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            obj.list.refresh_ordinals();
+            var node = nparams.treeitem_node;
             obj.map_tree[ 'acp_' + acp_item.id() ] =  node;
             if (params) {
                 for (var i in params) {
@@ -1807,12 +1810,12 @@ cat.copy_browser.prototype = {
                                         [ row.my.circ.target_copy() ]
                                     );
 
-                                    params.row_node.setAttribute( 'retrieve_id',row.my.acp.barcode() );
+                                    params.treeitem_node.setAttribute( 'retrieve_id',row.my.acp.barcode() );
 
                                 }
                             );
                         } else {
-                            params.row_node.setAttribute( 'retrieve_id',row.my.acp.barcode() );
+                            params.treeitem_node.setAttribute( 'retrieve_id',row.my.acp.barcode() );
                         }
                     */
                         obj.funcs.push(
@@ -1821,6 +1824,7 @@ cat.copy_browser.prototype = {
                                 if (typeof params.on_retrieve == 'function') {
                                     params.on_retrieve(row);
                                 }
+                                obj.list.refresh_ordinals();
 
                             }
                         );
@@ -1840,6 +1844,7 @@ cat.copy_browser.prototype = {
                         if (typeof window.xulG == 'object' && typeof window.xulG.on_select == 'function') {
                             window.xulG.on_select(list);
                         }
+                        obj.list.refresh_ordinals();
                     },
                     'on_dblclick' : function(ev) {
                         JSAN.use('util.functional');
@@ -1852,6 +1857,7 @@ cat.copy_browser.prototype = {
                         );
                         obj.toggle_actions();
                         util.widgets.dispatch('command','cmd_edit_items');
+                        obj.list.refresh_ordinals();
                     },
                     'on_select' : function(ev) {
                         JSAN.use('util.functional');
@@ -1868,6 +1874,7 @@ cat.copy_browser.prototype = {
                         if (typeof window.xulG == 'object' && typeof window.xulG.on_select == 'function') {
                             window.xulG.on_select(obj.sel_list);
                         }
+                        obj.list.refresh_ordinals();
                     },
                 }
             );
index 74fd2bf..47a9a28 100644 (file)
@@ -57,7 +57,7 @@ cat.copy_buckets.prototype = {
                         row.my.acn = blob.volume;
                         row.my.ahr = blob.hold;
                         row.my.circ = blob.circ;
-                        params.row_node.setAttribute('retrieve_id', js2JSON( [ blob.copy.id(), blob.copy.barcode(), row.my.bucket_item_id ] ));
+                        params.treeitem_node.setAttribute('retrieve_id', js2JSON( [ blob.copy.id(), blob.copy.barcode(), row.my.bucket_item_id ] ));
                         if (typeof params.on_retrieve == 'function') { params.on_retrieve(row); }
 
                     } catch(E) {
index 518b665..04df58d 100644 (file)
@@ -97,7 +97,7 @@ function init_list() {
             {
                 'retrieve_row' : function(params) {
                     if (params.row.my.bpbcm) {
-                        params.row_node.setAttribute('retrieve_id',params.row.my.bpbcm.id());
+                        params.treeitem_node.setAttribute('retrieve_id',params.row.my.bpbcm.id());
                     }
                     params.on_retrieve(params.row);
                     return params.row;
@@ -178,7 +178,7 @@ function handle_submit(create,my_bpbcm,my_barcode) {
         };
 
         if (barcode && rows[barcode]) {
-                var node = rows[barcode].my_node;
+                var node = rows[barcode].treeitem_node;
                 var parentNode = node.parentNode;
                 parentNode.removeChild( node );
                 delete(rows[barcode]);
@@ -400,7 +400,7 @@ function handle_remove() {
                             for (var i = 0; i < ids.length; i++) {
                                 var bpbcm_id = ids[i];
                                 try {
-                                    var node = rows[ bpbcm_barcode_map[ bpbcm_id ] ].my_node;
+                                    var node = rows[ bpbcm_barcode_map[ bpbcm_id ] ].treeitem_node;
                                     var parentNode = node.parentNode;
                                     parentNode.removeChild( node );
                                     delete(rows[ bpbcm_barcode_map[ bpbcm_id ] ]);
index a7da8d7..688f269 100644 (file)
@@ -718,21 +718,21 @@ cat.z3950.prototype = {
                                 }
                             }
                         );
-                        n.my_node.setAttribute('isbn', function(a){return a;}(obj.result_set[ obj.number_of_result_sets ].records[j].mvr).isbn());
-                        n.my_node.setAttribute(
+                        n.treeitem_node.setAttribute('isbn', function(a){return a;}(obj.result_set[ obj.number_of_result_sets ].records[j].mvr).isbn());
+                        n.treeitem_node.setAttribute(
                             'service',
                             function(a){return a;}(
                                 results[i].service
                             )
                         );
-                        n.my_node.setAttribute(
+                        n.treeitem_node.setAttribute(
                             'doc_id',
                             function(a){return a;}(
                                 (obj.result_set[ obj.number_of_result_sets ].records[j].mvr)
                             ).doc_id()
                         );
 
-                        if (!f) { n.my_node.parentNode.focus(); f = n; } 
+                        if (!f) { n.treeitem_node.parentNode.focus(); f = n; } 
                     }
                 } else {
                     x = document.createElement('description'); obj.controller.view.result_message.appendChild(x);
index 6605700..d628861 100644 (file)
@@ -508,7 +508,7 @@ circ.checkin.prototype = {
             try {
                 var row = params.row;
                 if (typeof params.on_retrieve == 'function') params.on_retrieve(row);
-                obj.update_no_change_label(params.my_node,row);
+                obj.update_no_change_label(params.treeitem_node,row);
                 var bill = row.my.mbts;
                 if (bill && document.getElementById('fine_tally') && ! row.already_tallied) {
                     params.row.already_tallied = true;
index c1fbbe4..af1c9b1 100644 (file)
@@ -1246,7 +1246,7 @@ circ.copy_status.prototype = {
                                     if (typeof obj.list_copyid_map[details.copy.id()][i] == 'undefined') {
                                         obj.list.append(params);
                                     } else {
-                                        params.my_node = obj.list_copyid_map[details.copy.id()][i].my_node;
+                                        params.treeitem_node = obj.list_copyid_map[details.copy.id()][i].treeitem_node;
                                         obj.list.refresh_row(params);
                                     }
                                 }
index 567dffb..5b98508 100644 (file)
@@ -547,13 +547,13 @@ function init_lists() {
                 function handle_props(row) {
                     try {
                         if ( row && row.my && row.my.mbts && Number( row.my.mbts.balance_owed() ) < 0 ) {
-                            util.widgets.addProperty(params.row_node.firstChild,'refundable');
-                            util.widgets.addProperty(params.row_node.firstChild.childNodes[ g.payment_pending_column_idx ],'refundable');
+                            util.widgets.addProperty(params.treeitem_node.firstChild,'refundable');
+                            util.widgets.addProperty(params.treeitem_node.firstChild.childNodes[ g.payment_pending_column_idx ],'refundable');
                         }
                         if ( row && row.my && row.my.circ && ! row.my.circ.checkin_time() ) {
                             $('circulating_hint').hidden = false;
-                            util.widgets.addProperty(params.row_node.firstChild,'circulating');
-                            util.widgets.addProperty(params.row_node.firstChild.childNodes[ g.title_column_idx ],'circulating');
+                            util.widgets.addProperty(params.treeitem_node.firstChild,'circulating');
+                            util.widgets.addProperty(params.treeitem_node.firstChild.childNodes[ g.title_column_idx ],'circulating');
                         }
                     } catch(E) {
                         g.error.sdump('D_WARN','Error setting list properties in bill2.js: ' + E);
index ccee8c9..28f2cf7 100644 (file)
@@ -126,7 +126,7 @@ patron.holds.prototype = {
                                     }
 
                                     obj.holds_map[ row.my.ahr.id() ] = blob;
-                                    params.row_node.setAttribute('retrieve_id',
+                                    params.treeitem_node.setAttribute('retrieve_id',
                                         js2JSON({
                                             'copy_id':copy_id,
                                             'barcode':row.my.acp ? row.my.acp.barcode() : null,
index cf9394c..ee7215e 100644 (file)
@@ -745,7 +745,7 @@ patron.items.prototype = {
                                         if (typeof robj.copy == 'object' && robj.copy != null) copy_id = robj.copy.id();
                                 }
                                 
-                                params.row_node.setAttribute( 'retrieve_id', js2JSON({'copy_id':copy_id,'circ_id':row.my.circ.id(),'barcode':row.my.acp.barcode(),'doc_id': ( row.my.record ? row.my.record.id() : null ) }) );
+                                params.treeitem_node.setAttribute( 'retrieve_id', js2JSON({'copy_id':copy_id,'circ_id':row.my.circ.id(),'barcode':row.my.acp.barcode(),'doc_id': ( row.my.record ? row.my.record.id() : null ) }) );
             
                                 if (typeof params.on_retrieve == 'function') {
                                     params.on_retrieve(row);
@@ -767,7 +767,7 @@ patron.items.prototype = {
                             if (typeof row.my.acp == 'object' && row.my.acp != null) copy_id = row.my.acp.id();
                     }
  
-                    params.row_node.setAttribute( 'retrieve_id', js2JSON({'copy_id':row.my.acp.id(),'circ_id':row.my.circ.id(),'barcode':row.my.acp.barcode(),'doc_id': (row.my.record ? row.my.record.id() : null) }) );
+                    params.treeitem_node.setAttribute( 'retrieve_id', js2JSON({'copy_id':row.my.acp.id(),'circ_id':row.my.circ.id(),'barcode':row.my.acp.barcode(),'doc_id': (row.my.record ? row.my.record.id() : null) }) );
                     if (typeof params.on_retrieve == 'function') {
                         params.on_retrieve(row);
                     }
@@ -834,7 +834,7 @@ patron.items.prototype = {
         try {
             var nparams = obj.list_circ_map[circ_id];
             if (move_to_bottom_list) { 
-                obj.list_circ_map[circ_id].my_node.setAttribute('hidden','true');
+                obj.list_circ_map[circ_id].treeitem_node.setAttribute('hidden','true');
                 var nparams2 = obj.list2.append( { 'row' : { 'my' : { 'circ_id' : circ_id } },  'to_bottom' : true, 'which_list' : 1 } );
                 obj.list_circ_map[circ_id] = nparams2; 
             } else {
index 706d449..a5a9d3e 100644 (file)
@@ -116,7 +116,7 @@ function cancel(ids) {
                     if (idx == ids.length) { pm.value = 0; pm.hidden = true; }
                     var robj = req.getResultObject();
                     if (robj == '1') {
-                        var node = rows[ row_id_usrname_map[ id ] ].my_node;
+                        var node = rows[ row_id_usrname_map[ id ] ].treeitem_node;
                         var parentNode = node.parentNode;
                         parentNode.removeChild( node );
                         delete(rows[ row_id_usrname_map[ id ] ]);
@@ -174,7 +174,7 @@ function load( usrnames ) {
         function gen_on_save_handler(usrname) {
             return function() {
                 try {
-                    var node = rows[ usrname ].my_node;
+                    var node = rows[ usrname ].treeitem_node;
                     var parentNode = node.parentNode;
                     parentNode.removeChild( node );
                     delete(row_id_usrname_map[ rows[ usrname ].row.my.stgu.row_id() ]);
@@ -224,7 +224,7 @@ function init_list() {
 
 function retrieve_row(params) { // callback function for fleshing rows in a list
     try {
-        params.row_node.setAttribute('retrieve_id',js2JSON( { 'row_id' : params.row.my.stgu.row_id(), 'usrname' : params.row.my.stgu.usrname() } )); 
+        params.treeitem_node.setAttribute('retrieve_id',js2JSON( { 'row_id' : params.row.my.stgu.row_id(), 'usrname' : params.row.my.stgu.usrname() } )); 
         params.on_retrieve(params.row); 
     } catch(E) {
         alert('Error in staged.js, retrieve_row(): ' + E);
index f72d086..b2e6b2c 100644 (file)
@@ -85,7 +85,7 @@ function init_archived_list() {
 
 
 function retrieve_row (params) { // callback function for fleshing rows in a list
-    params.row_node.setAttribute('retrieve_id',params.row.my.ausp.id()); 
+    params.treeitem_node.setAttribute('retrieve_id',params.row.my.ausp.id()); 
     params.on_retrieve(params.row); 
     return params.row; 
 }
@@ -255,7 +255,7 @@ function generate_penalty_remove_function(id) {
             if (typeof req.ilsevent != 'undefined' || String(req) != '1') {
                 error.standard_unexpected_error_alert(patronStrings.getFormattedString('staff.patron.standing_penalty.remove_error',[id]),req);
             } else {
-                var node = rows[ id ].my_node;
+                var node = rows[ id ].treeitem_node;
                 var parentNode = node.parentNode;
                 parentNode.removeChild( node );
                 delete(rows[ id ]);
@@ -371,7 +371,7 @@ function handle_archive_penalty(ev) {
                             try {
                                 var res = openils.Util.readResponse(r,true);
                                 /* FIXME - test for success */
-                                var node = rows[row_id].my_node;
+                                var node = rows[row_id].treeitem_node;
                                 var parentNode = node.parentNode;
                                 parentNode.removeChild( node );
                                 delete(rows[row_id]);
index 7238536..29c3fa2 100644 (file)
@@ -158,7 +158,7 @@ function init_list() {
 }
 
 function retrieve_row(params) { // callback function for fleshing rows in a list
-    params.row_node.setAttribute('retrieve_id',params.row.my.atev.id()); 
+    params.treeitem_node.setAttribute('retrieve_id',params.row.my.atev.id()); 
     params.on_retrieve(params.row); 
     return params.row; 
 }
index 8d56f60..f3f0823 100644 (file)
@@ -928,7 +928,7 @@ serial.manage_dists.prototype = {
                 data.node = obj.map_tree[ 'aou_' + parent_org.id() ];
             }
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            var node = nparams.treeitem_node;
             if (params) {
                 for (var i in params) {
                     node.setAttribute(i,params[i]);
@@ -993,7 +993,7 @@ serial.manage_dists.prototype = {
                 'no_auto_select' : true,
             };
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            var node = nparams.treeitem_node;
             obj.map_tree[ 'sdist_' + sdist_tree.id() ] =  node;
             if (params) {
                 for (var i in params) {
@@ -1017,7 +1017,7 @@ serial.manage_dists.prototype = {
                 'no_auto_select' : true,
             };
             nparams = obj.list.append(sstr_group_node_data);
-            obj.map_tree[ 'sdist_sstr_group_' + sdist_tree.id() ] =  nparams.my_node;
+            obj.map_tree[ 'sdist_sstr_group_' + sdist_tree.id() ] =  nparams.treeitem_node;
         } catch(E) {
             dump(E+'\n');
             alert(E);
@@ -1058,7 +1058,7 @@ serial.manage_dists.prototype = {
             };
             data['row']['my'][type] = item; // TODO: future optimization: get only the IDs of these leaves, then fetch the full row in 'retrieve_row'
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            var node = nparams.treeitem_node;
             obj.map_tree[ type + '_' + sdist_tree.id() + '_' + item.id() ] =  node;
             if (label) {
                 data['row']['my']['label'] = label;
index 5e734ff..ec56204 100644 (file)
@@ -736,8 +736,8 @@ serial.manage_items.prototype = {
                             var sitem = robj[0];
                             obj.list_sitem_map[sitem.id()] = sitem;
                             row.my.sitem = sitem;
-                            //params.row_node.setAttribute( 'retrieve_id', js2JSON({'copy_id':copy_id,'circ_id':row.my.circ.id(),'barcode':row.my.acp.barcode(),'doc_id': ( row.my.record ? row.my.record.id() : null ) }) );
-                            params.row_node.setAttribute( 'retrieve_id', js2JSON({'sitem_id':sitem.id()}) );
+                            //params.treeitem_node.setAttribute( 'retrieve_id', js2JSON({'copy_id':copy_id,'circ_id':row.my.circ.id(),'barcode':row.my.acp.barcode(),'doc_id': ( row.my.record ? row.my.record.id() : null ) }) );
+                            params.treeitem_node.setAttribute( 'retrieve_id', js2JSON({'sitem_id':sitem.id()}) );
                             dump('dumping... ' + js2JSON(obj.list_sitem_map[sitem.id()]));
                             if (typeof params.on_retrieve == 'function') {
                                 params.on_retrieve(row);
index fa5c4c6..74ca397 100644 (file)
@@ -1352,7 +1352,7 @@ serial.manage_subs.prototype = {
                 data.node = obj.map_tree[ 'aou_' + parent_org.id() ];
             }
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            var node = nparams.treeitem_node;
             if (params) {
                 for (var i in params) {
                     node.setAttribute(i,params[i]);
@@ -1418,7 +1418,7 @@ serial.manage_subs.prototype = {
                 'no_auto_select' : true,
             };
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            var node = nparams.treeitem_node;
             obj.map_tree[ 'ssub_' + ssub_tree.id() ] =  node;
             if (params) {
                 for (var i in params) {
@@ -1445,7 +1445,7 @@ serial.manage_subs.prototype = {
                 'no_auto_select' : true,
             };
             nparams = obj.list.append(sdist_group_node_data);
-            obj.map_tree[ 'ssub_sdist_group_' + ssub_tree.id() ] =  nparams.my_node;
+            obj.map_tree[ 'ssub_sdist_group_' + ssub_tree.id() ] =  nparams.treeitem_node;
 
             var siss_group_node_data = {
                 'row' : {
@@ -1459,7 +1459,7 @@ serial.manage_subs.prototype = {
                 'no_auto_select' : true,
             };
             nparams = obj.list.append(siss_group_node_data);
-            obj.map_tree[ 'ssub_siss_group_' + ssub_tree.id() ] =  nparams.my_node;
+            obj.map_tree[ 'ssub_siss_group_' + ssub_tree.id() ] =  nparams.treeitem_node;
 
             var scap_group_node_data = {
                 'row' : {
@@ -1473,7 +1473,7 @@ serial.manage_subs.prototype = {
                 'no_auto_select' : true,
             };
             nparams = obj.list.append(scap_group_node_data);
-            obj.map_tree[ 'ssub_scap_group_' + ssub_tree.id() ] =  nparams.my_node;
+            obj.map_tree[ 'ssub_scap_group_' + ssub_tree.id() ] =  nparams.treeitem_node;
         } catch(E) {
             dump(E+'\n');
             alert(E);
@@ -1510,7 +1510,7 @@ serial.manage_subs.prototype = {
             };
             data['row']['my'][type] = item; // TODO: future optimization: get only the IDs of these leaves, then fetch the full row in 'retrieve_row'
             var nparams = obj.list.append(data);
-            var node = nparams.my_node;
+            var node = nparams.treeitem_node;
             obj.map_tree[ type + '_' + item.id() ] =  node;
             if (attributes) {
                 for (var i in attributes) {
index 1c9da3e..ee08b6c 100644 (file)
@@ -19,6 +19,13 @@ treechildren::-moz-tree-row(selected) {
     border: thin dashed lightblue ! important;
 }
 
+treechildren::-moz-tree-column(ordinal) {
+    background: -moz-dialog ! important;
+}
+
+treechildren::-moz-tree-cell-text(ordinal) {
+    font-weight: bold ! important;
+}
 /*
 treechildren::-moz-tree-cell-text(selected,focus) {
     color: black;