From 630536fa7619bf7225eba26c949ec58ab575de3c Mon Sep 17 00:00:00 2001 From: phasefx Date: Thu, 19 Feb 2009 05:39:53 +0000 Subject: [PATCH] Merged revisions 12025,12028,12035,12037-12050,12053-12057,12060-12065,12067-12072,12074,12077-12100,12103-12114,12117-12125,12127,12131-12134,12137-12147,12149-12154,12156-12162,12164-12174,12177-12187,12189-12197,12200-12211,12213-12218,12223,12227 via svnmerge from svn://svn.open-ils.org/ILS/trunk ........ r12025 | miker | 2009-01-31 17:04:33 -0500 (Sat, 31 Jan 2009) | 1 line non-grouped event firing, event locating and grouping ........ r12028 | miker | 2009-01-31 23:42:59 -0500 (Sat, 31 Jan 2009) | 1 line adding event group support ........ r12035 | scottmk | 2009-02-01 14:03:30 -0500 (Sun, 01 Feb 2009) | 8 lines Rearranged a bit for clarity. 1. In doRetrieve: don't declare obj until we're ready to build it. 2. In doFieldmapperSearch(): reversed an "if" test to clarify what's an early return and what's mainline logic. Also: don't declare or create res_list until we know we're going to return it. ........ r12037 | scottmk | 2009-02-01 16:26:36 -0500 (Sun, 01 Feb 2009) | 3 lines Replaced all occurrences of the macro OSRF_METHOD_VERIFY_CONTEXT with equivalent calls to the function osrfMethodVerifyContext(). ........ r12038 | miker | 2009-02-01 16:34:34 -0500 (Sun, 01 Feb 2009) | 1 line need the full hook, not just idlist ........ r12039 | miker | 2009-02-01 16:35:44 -0500 (Sun, 01 Feb 2009) | 1 line keys is more efficient than map, since we already have the id list as the hash keys ........ r12040 | dbs | 2009-02-02 01:53:55 -0500 (Mon, 02 Feb 2009) | 7 lines Add 98 Nepali MARC records to the test datasets On a stock OpenSRF 1.0.3 / Evergreen 1.4.0.1 machine, only 20 of these records are successfully added using the standard marc2bre / direct_ingest / pg_loader toolchain. Looks like errors decoding JSON containing exotic characters; also some strange fingerprints. ........ r12041 | dbs | 2009-02-02 02:02:04 -0500 (Mon, 02 Feb 2009) | 2 lines It's important to mark this as application/octet-stream ........ r12042 | dbs | 2009-02-02 15:12:10 -0500 (Mon, 02 Feb 2009) | 2 lines This kind of typo might cause headaches ........ r12043 | erickson | 2009-02-03 11:26:58 -0500 (Tue, 03 Feb 2009) | 1 line added a cascading survey delete operation. ........ r12044 | dbs | 2009-02-03 11:42:14 -0500 (Tue, 03 Feb 2009) | 1 line Initial attempt to add acquisitions to default configuration ........ r12045 | erickson | 2009-02-03 12:33:51 -0500 (Tue, 03 Feb 2009) | 1 line fixed streaming vs. non-streaming logic error in currency_type retrieve. took opportunity to port to pcrud ........ r12046 | scottmk | 2009-02-03 12:53:48 -0500 (Tue, 03 Feb 2009) | 5 lines Added a couple of sanity checks to SELECT(). Return NULL if (1) the input jsonObject for the join tree is neither a hash, nor an array, nor a string; or (2) it's a hash with more than one element. ........ r12047 | dbs | 2009-02-03 13:12:43 -0500 (Tue, 03 Feb 2009) | 1 line Make pcrud a publicly available service, just like permacrud ........ r12048 | erickson | 2009-02-03 14:24:16 -0500 (Tue, 03 Feb 2009) | 1 line added pcrud entries for exchange_rate ........ r12049 | dbs | 2009-02-03 15:49:54 -0500 (Tue, 03 Feb 2009) | 3 lines Dojo wants locales passed to it in xx-yy format, not xx-YY. Closes #39. ........ r12050 | dbs | 2009-02-03 15:54:03 -0500 (Tue, 03 Feb 2009) | 3 lines Dojo wants locales in xx-yy format, not xx-YY. Avoids #39 + 1. ........ r12053 | scottmk | 2009-02-03 17:44:48 -0500 (Tue, 03 Feb 2009) | 13 lines Minor tweaks to the SELECT function: 1. Narrowed the scope of the idlClass variable. 2. Made cname const, 3. In the loop for building the column list: eliminated an IDL lookup for the class name, because we already have it. The IDL stores it redundantly in two different places, and we don't need to find both. 4. Renamed __column and __alias to _column and _alias. Identifiers beginning with two underscores are reserved. ........ r12054 | scottmk | 2009-02-03 20:43:25 -0500 (Tue, 03 Feb 2009) | 7 lines This update tweaks the SELECT function, at the point commented "make sure the target relation is in the join tree". The new logic should be equivalent to the old, except that the old code potentially invokes undefined behavior by reading through a null pointer. ........ r12055 | dbs | 2009-02-03 23:37:46 -0500 (Tue, 03 Feb 2009) | 2 lines And now make old-school gateway requests behave in the OPAC. ........ r12056 | erickson | 2009-02-03 23:46:55 -0500 (Tue, 03 Feb 2009) | 1 line allow caller to pass additional onpostapply and oncancel handlers ........ r12057 | erickson | 2009-02-03 23:47:38 -0500 (Tue, 03 Feb 2009) | 1 line better handle case where grid structure may exist but just have no columns yet defined ........ r12060 | erickson | 2009-02-03 23:49:23 -0500 (Tue, 03 Feb 2009) | 1 line added some rough exchange rate editing code using auto widgets. essentially functions, but more later ........ r12061 | erickson | 2009-02-04 10:41:15 -0500 (Wed, 04 Feb 2009) | 1 line added pending column to represent pending approval from staff. ........ r12062 | erickson | 2009-02-04 11:12:52 -0500 (Wed, 04 Feb 2009) | 1 line added pcrud as controller for asva ........ r12063 | erickson | 2009-02-04 11:43:03 -0500 (Wed, 04 Feb 2009) | 1 line auto-grid now has the ability to pop up an edit dialog for the given fieldmapper object ........ r12064 | scottmk | 2009-02-04 11:48:49 -0500 (Wed, 04 Feb 2009) | 4 lines In SELECT(): simplify and clarify the logic for building a default select list. Also, for a minor performance boost: don't look up the core fields unless we're actually going to use them. ........ r12065 | erickson | 2009-02-04 13:41:44 -0500 (Wed, 04 Feb 2009) | 1 line use built-in edit dialog ........ r12067 | erickson | 2009-02-04 16:27:44 -0500 (Wed, 04 Feb 2009) | 1 line rely on the stored grid data to reconstitute a fm object for building the edit dialog ........ r12068 | erickson | 2009-02-04 16:28:53 -0500 (Wed, 04 Feb 2009) | 1 line fixed bug where subsequent dialogs were appending to the the class-level fieldList array. can now hit Enter to save instead of having to click Save ........ r12069 | scottmk | 2009-02-04 16:49:06 -0500 (Wed, 04 Feb 2009) | 7 lines In SELECT(): further rewrote, and festooned with comments, the code that verifies that every column in the SELECT clause comes from a class in the FROM clause. Also, for readability: reversed an "if" test that treats functions differently from classes. ........ r12070 | erickson | 2009-02-04 17:50:23 -0500 (Wed, 04 Feb 2009) | 1 line when using the popup dialog, overide the default focus handling. this is still experimental ........ r12071 | erickson | 2009-02-04 17:51:11 -0500 (Wed, 04 Feb 2009) | 1 line no need to import 2 grid css's, nor the private _grid css ........ r12072 | erickson | 2009-02-04 23:36:08 -0500 (Wed, 04 Feb 2009) | 1 line selected-ness follow mouse and key navigation in a uniform fashion. ........ r12074 | erickson | 2009-02-04 23:48:35 -0500 (Wed, 04 Feb 2009) | 1 line cleaned up to match latest autogrid ........ r12077 | erickson | 2009-02-05 09:36:42 -0500 (Thu, 05 Feb 2009) | 1 line added option to launch creation dialog from auto grid. passing final pcrud response to onPostSubmit handler ........ r12078 | erickson | 2009-02-05 11:03:25 -0500 (Thu, 05 Feb 2009) | 1 line give dojo a td and it will replace it with the dijit node, so give dojo a sub-node to clobber instead ........ r12079 | erickson | 2009-02-05 11:04:36 -0500 (Thu, 05 Feb 2009) | 1 line start of single label + action bar to sit above the grid ........ r12080 | erickson | 2009-02-05 11:21:43 -0500 (Thu, 05 Feb 2009) | 1 line allow for definition of default cell width. (can still be overridden by defining cell widths in the grid) ........ r12081 | scottmk | 2009-02-05 11:35:44 -0500 (Thu, 05 Feb 2009) | 18 lines Various tweaks, mainly to the SELECT function. 1. Moved some IDL lookups out of a loop where their results were loop invariants. 2. Narrowed the scope of _alias. 3. Eliminated some calls to jsonObjectToSimpleString() and strdup(), along with the associated mallocs and frees. 4. Eliminated a couple of needlessly repeated calls to jsonObjectGetKey() by caching the results of the first ones. 5. Uncommented a couple of commented-out calls to jsonIteratorFree(), because I don't see anything wrong with them. 6. Moved another commented-out call back into a scope where it would compile if uncommented (but left it commented out for now). ........ r12082 | erickson | 2009-02-05 12:52:24 -0500 (Thu, 05 Feb 2009) | 1 line more style tidying ........ r12083 | erickson | 2009-02-05 12:53:05 -0500 (Thu, 05 Feb 2009) | 1 line consistent focus handling after user clicks cancel/save in edit dialog. still funky when same actions are done from the create dialog ........ r12084 | phasefx | 2009-02-05 13:25:39 -0500 (Thu, 05 Feb 2009) | 1 line output the deleted flag from the bre ........ r12085 | erickson | 2009-02-05 17:00:41 -0500 (Thu, 05 Feb 2009) | 1 line added selector attr to currency type ........ r12086 | erickson | 2009-02-05 18:10:29 -0500 (Thu, 05 Feb 2009) | 1 line repaired old pylons-ism with TT context locale. note, the locale will need some cleaning-up in EGWeb.pm to be wholly functional ........ r12087 | erickson | 2009-02-05 19:08:37 -0500 (Thu, 05 Feb 2009) | 1 line added support for auto-building select options from has_a links, using the selector attribute when defined. mostly functional, needs some tweaking ........ r12088 | miker | 2009-02-05 21:07:42 -0500 (Thu, 05 Feb 2009) | 1 line adding hold-cancel cause and note table and fields; teaching hold targeter how to use the cancel_cause field ........ r12089 | scottmk | 2009-02-06 04:32:44 -0500 (Fri, 06 Feb 2009) | 11 lines Miscellaneous tweaks: 1. In searchFieldTransform(): simplified the way we append subcolumns, to reduce the churning of memory. 2. I found it confusing that we were using _column for two different (though similar) things. So I split it into two separate variables in two separate scopes: col_name and _column. 3. Don't bother looking up "i18n" if it's disabled anyway. ........ r12090 | miker | 2009-02-06 09:28:30 -0500 (Fri, 06 Feb 2009) | 1 line add patron-opac cause, and comments about IDs ........ r12091 | miker | 2009-02-06 10:05:41 -0500 (Fri, 06 Feb 2009) | 1 line teaching everyone how to note the pkey delete restriction policy for specific classes ........ r12092 | miker | 2009-02-06 10:24:02 -0500 (Fri, 06 Feb 2009) | 1 line /actually/ pin the ids, and set the sequence appropriately ........ r12093 | erickson | 2009-02-06 10:42:12 -0500 (Fri, 06 Feb 2009) | 1 line fixed problem with async widget building and saving (by using my own code as it was meant to be used) ........ r12094 | erickson | 2009-02-06 10:52:21 -0500 (Fri, 06 Feb 2009) | 1 line added support for passing in cancellation cause and note ........ r12095 | erickson | 2009-02-06 10:55:06 -0500 (Fri, 06 Feb 2009) | 1 line passing hold cancellation reason from opac-cancelled holds ........ r12096 | erickson | 2009-02-06 11:15:14 -0500 (Fri, 06 Feb 2009) | 1 line added support for displaying the last X cancelled holds, most recently cancelled holds first ........ r12097 | erickson | 2009-02-06 12:52:57 -0500 (Fri, 06 Feb 2009) | 1 line new method for un-canceling holds. request_time and expire_time are reset if appropriate org-unit-setting is enabled ........ r12098 | miker | 2009-02-06 12:58:04 -0500 (Fri, 06 Feb 2009) | 1 line use method_lookup instead of direct call ($self is not what you think...) ........ r12099 | erickson | 2009-02-06 17:11:32 -0500 (Fri, 06 Feb 2009) | 1 line don't attempt to retrieve the linked selector objects if the data is not retrievable via pcrud ........ r12100 | dbs | 2009-02-07 14:01:44 -0500 (Sat, 07 Feb 2009) | 1 line Make all FKs deferrable again ........ r12103 | scottmk | 2009-02-07 19:28:13 -0500 (Sat, 07 Feb 2009) | 16 lines More tweaks, mostly to SELECT(): 1. When building the select list, insert a separator comma in one place instead of duplicating the code. 2. Narrowed the scope of fname. 3. Created a new transform_str variable instead of reusing _column for a different purpose. 4. Juggled _column and _alias a bit so as to eliminate a strdup() and a free(). 5.In searchFieldTransform(): plugged a memory leak in the event of an error return. ........ r12104 | miker | 2009-02-07 21:12:31 -0500 (Sat, 07 Feb 2009) | 1 line adding note tables for all bucket and item tables; adding pos field for optional ordering of items within a bucket ........ r12105 | miker | 2009-02-07 21:33:03 -0500 (Sat, 07 Feb 2009) | 1 line adding IDL for note tables for all bucket and item tables; adding IDL for pos field for optional ordering of items within a bucket ........ r12106 | miker | 2009-02-07 21:37:56 -0500 (Sat, 07 Feb 2009) | 1 line add a record bucket type for reading lists ........ r12107 | erickson | 2009-02-08 09:43:43 -0500 (Sun, 08 Feb 2009) | 1 line more provider data. address, contact, and contact address ........ r12108 | erickson | 2009-02-08 09:59:49 -0500 (Sun, 08 Feb 2009) | 1 line no need to publish provider contacts in reporter-store ........ r12109 | erickson | 2009-02-08 11:05:20 -0500 (Sun, 08 Feb 2009) | 1 line autogrid can now fetch and load all data of a given class ........ r12110 | erickson | 2009-02-08 11:06:36 -0500 (Sun, 08 Feb 2009) | 1 line use new built-in autogrid fetch & load ........ r12111 | erickson | 2009-02-08 20:05:05 -0500 (Sun, 08 Feb 2009) | 1 line init datalist at startup so each successive instance isn't appending to the same array ........ r12112 | miker | 2009-02-08 22:34:12 -0500 (Sun, 08 Feb 2009) | 1 line IE does not yet support Array.forEach ... so we fake it with dojo ........ r12113 | miker | 2009-02-08 22:34:48 -0500 (Sun, 08 Feb 2009) | 1 line replace the (very slow) view with the materialized version ........ r12114 | erickson | 2009-02-09 09:12:41 -0500 (Mon, 09 Feb 2009) | 1 line function to wrap up a xact-base storage request in 1 call ........ r12117 | erickson | 2009-02-09 12:56:19 -0500 (Mon, 09 Feb 2009) | 1 line teach the IDL about the existence of pkey sequences ........ r12118 | erickson | 2009-02-09 12:56:49 -0500 (Mon, 09 Feb 2009) | 1 line not sure why views[1] was working before, but it's not now ........ r12119 | erickson | 2009-02-09 13:09:43 -0500 (Mon, 09 Feb 2009) | 1 line removed some unnecessary console logs ........ r12120 | erickson | 2009-02-09 13:47:25 -0500 (Mon, 09 Feb 2009) | 1 line can now specify post update and create handlers. wrap re-focus in try ........ r12121 | erickson | 2009-02-09 13:51:24 -0500 (Mon, 09 Feb 2009) | 1 line moved currency type config into conify, using autogrid ........ r12122 | erickson | 2009-02-09 16:53:48 -0500 (Mon, 09 Feb 2009) | 1 line created read-only version of a single widget and edit pane ........ r12123 | erickson | 2009-02-09 16:54:27 -0500 (Mon, 09 Feb 2009) | 1 line by default, don't show sequence columns, which will usually be 'id' columns. configurable ........ r12124 | djfiander | 2009-02-09 21:52:54 -0500 (Mon, 09 Feb 2009) | 1 line A bunch of untested code to support serials predictions ........ r12125 | scottmk | 2009-02-09 22:46:33 -0500 (Mon, 09 Feb 2009) | 15 lines Miscellaneous tweaks: 1. Simplified the logic in searchValueTransform(). 2. In a debug message: changed format specification to %p for pointer values, instead of %d. Using %d will garble the output if ints and pointers don't have the same size (and they don't on my laptop). 3. Corrected a typo in another message. The word "non-existant" is non-existent. 4. For clarity: in several places, reversed the logic of some if/elses so that we test for a positive condition instead of a negative condition. ........ r12127 | erickson | 2009-02-10 12:05:53 -0500 (Tue, 10 Feb 2009) | 1 line sort bucket items by 'pos' field ........ r12131 | erickson | 2009-02-10 13:17:32 -0500 (Tue, 10 Feb 2009) | 1 line forward port craftsman skin : svn merge -r12127:12130 svn://svn.open-ils.org/ILS/branches/rel_1_4 ........ r12132 | erickson | 2009-02-10 14:06:53 -0500 (Tue, 10 Feb 2009) | 1 line flesh item notes on retrieval. refactor retrieval to use cstoreditor ........ r12133 | erickson | 2009-02-10 14:08:51 -0500 (Tue, 10 Feb 2009) | 1 line added bucket item notes virtual fields ........ r12134 | dbs | 2009-02-10 14:16:33 -0500 (Tue, 10 Feb 2009) | 2 lines Get the real org_unit_type depth from aout ........ r12137 | erickson | 2009-02-10 14:41:10 -0500 (Tue, 10 Feb 2009) | 1 line handle item notes on cascade delete. created item note CUD method. more cstoreditor-ification ........ r12138 | erickson | 2009-02-10 15:46:08 -0500 (Tue, 10 Feb 2009) | 1 line handle case where an address is pending but replaces no other address ........ r12139 | erickson | 2009-02-10 15:46:31 -0500 (Tue, 10 Feb 2009) | 1 line entry for usr address pending flag ........ r12140 | erickson | 2009-02-10 15:46:58 -0500 (Tue, 10 Feb 2009) | 1 line set pending true on altered addresses ........ r12141 | erickson | 2009-02-10 15:49:11 -0500 (Tue, 10 Feb 2009) | 1 line return addr id for consistency ........ r12142 | erickson | 2009-02-10 15:51:55 -0500 (Tue, 10 Feb 2009) | 1 line allow approval of pending address, even if it does not replace any other address ........ r12143 | erickson | 2009-02-10 16:24:39 -0500 (Tue, 10 Feb 2009) | 1 line take advantage of the new billable_transaction_summary table. also, force caller to make_mbts to pass an editor in ........ r12144 | erickson | 2009-02-10 17:44:51 -0500 (Tue, 10 Feb 2009) | 1 line initial support for adding entries to a patron's items-checked-out list ........ r12145 | scottmk | 2009-02-10 23:12:08 -0500 (Tue, 10 Feb 2009) | 9 lines 1. Applied a uniform standard to determine whether a string represents true or false. A few remaining spots are unchanged for now because they are dubious on other grounds. 2. In one spot: replaced jsonParseString("true") with jsonNewBoolObject( 1 ). 3. In oilsMakeFieldmapperFromResult(): broke up a confusing "if" test into two simpler ones. ........ r12146 | dbs | 2009-02-10 23:15:19 -0500 (Tue, 10 Feb 2009) | 2 lines Doh! slight HTML problem ........ r12147 | erickson | 2009-02-10 23:33:16 -0500 (Tue, 10 Feb 2009) | 1 line plugged in checkout history bucket population to checkout process ........ r12149 | miker | 2009-02-11 12:02:19 -0500 (Wed, 11 Feb 2009) | 1 line fix off-by-one on function literal construction (for function-in-from and literal value transforms); use the function name as the from clause alias, avoiding "(null)" as the alias ........ r12150 | scottmk | 2009-02-11 13:55:11 -0500 (Wed, 11 Feb 2009) | 7 lines 1. Corrected the enforcement of the readonly attribute for classes. It was backwards, but harmlessly so because the enforcement is redundant. 2. Reversed the default for the "virtual" attribute of a field. Formerly it defaulted to true; now it defaults to false. ........ r12151 | erickson | 2009-02-11 15:43:00 -0500 (Wed, 11 Feb 2009) | 1 line use copy buckets instead (for precats) and verify the copy bucket mechanism is globally configured ........ r12152 | erickson | 2009-02-11 16:18:28 -0500 (Wed, 11 Feb 2009) | 1 line fixed broken array ref handling ........ r12153 | miker | 2009-02-11 16:31:05 -0500 (Wed, 11 Feb 2009) | 1 line add idl field documenation table and accompanying IDL entry ... crazy recursive docs ........ r12154 | miker | 2009-02-11 16:33:10 -0500 (Wed, 11 Feb 2009) | 1 line no, we want that, really ........ r12156 | miker | 2009-02-12 01:31:32 -0500 (Thu, 12 Feb 2009) | 1 line adding IN subquery support ........ r12157 | erickson | 2009-02-12 11:33:20 -0500 (Thu, 12 Feb 2009) | 1 line fixed wiget value accessor/mutator which resulted in values with embedded wiget objects (resulting in infinite recursion in js2JSON) ........ r12158 | erickson | 2009-02-12 12:01:20 -0500 (Thu, 12 Feb 2009) | 1 line new provider ui, based on autogrid and moved into conify (editing static data). leaving other in place until all functionality is ported over ........ r12159 | erickson | 2009-02-12 12:01:53 -0500 (Thu, 12 Feb 2009) | 1 line slight reformatting for readability ........ r12160 | erickson | 2009-02-12 12:25:40 -0500 (Thu, 12 Feb 2009) | 1 line added pcrud entries for trigger event def and hook ........ r12161 | dbs | 2009-02-12 12:27:04 -0500 (Thu, 12 Feb 2009) | 2 lines Revert r12071 until we port jubgrid.js over to the Dojo 1.2 DataGrid ........ r12162 | scottmk | 2009-02-12 14:25:37 -0500 (Thu, 12 Feb 2009) | 2 lines Pass down an osrfMethodContext* where we need one. ........ r12164 | dbs | 2009-02-12 15:15:35 -0500 (Thu, 12 Feb 2009) | 2 lines Further testing suggests that dojox/resources/Grid.css is not needed, and in fact causes problems ........ r12165 | erickson | 2009-02-12 17:12:54 -0500 (Thu, 12 Feb 2009) | 1 line pcrud entries for action_trigger validator and reactor ........ r12166 | erickson | 2009-02-12 17:22:22 -0500 (Thu, 12 Feb 2009) | 1 line repaired fkey column names ........ r12167 | erickson | 2009-02-12 17:37:01 -0500 (Thu, 12 Feb 2009) | 1 line no need for a store if there is no tree data (don't attempt to access dataList[0]) ........ r12168 | dbs | 2009-02-13 00:15:26 -0500 (Fri, 13 Feb 2009) | 2 lines Add VIEW_BILLING_TYPE permission to Staff ........ r12169 | erickson | 2009-02-13 09:19:03 -0500 (Fri, 13 Feb 2009) | 1 line pcrud for action_trigger.cleanup ........ r12170 | erickson | 2009-02-13 11:14:40 -0500 (Fri, 13 Feb 2009) | 1 line func comments, treat "" as null ........ r12171 | erickson | 2009-02-13 11:15:14 -0500 (Fri, 13 Feb 2009) | 1 line don't display auto-generated fields in create dialog ........ r12172 | erickson | 2009-02-13 11:15:58 -0500 (Fri, 13 Feb 2009) | 1 line when no field order is defined but fields existin in the markup, start there with the order ........ r12173 | erickson | 2009-02-13 11:17:21 -0500 (Fri, 13 Feb 2009) | 1 line UI for managing trigger event_defs, hooks, reactors, and validators -- all auto-generated at this point, but will need some additions ........ r12174 | dbs | 2009-02-13 12:03:05 -0500 (Fri, 13 Feb 2009) | 3 lines Give the Administrator user an explicit work_ou of 1 (Consortium) Fixes calls to open-ils.actor.user.work_perm.highest_org_set.batch and friends that returned NULL for Administrator user ........ r12177 | erickson | 2009-02-13 14:58:24 -0500 (Fri, 13 Feb 2009) | 1 line fixed fkey col name ........ r12178 | erickson | 2009-02-13 15:08:30 -0500 (Fri, 13 Feb 2009) | 1 line fixed org unit data type ........ r12179 | erickson | 2009-02-13 17:10:08 -0500 (Fri, 13 Feb 2009) | 1 line can now override any given auto-widget with a locally defined widget ........ r12180 | erickson | 2009-02-13 17:21:34 -0500 (Fri, 13 Feb 2009) | 1 line UI for manging idl_field_doc entries ........ r12181 | miker | 2009-02-13 20:11:31 -0500 (Fri, 13 Feb 2009) | 1 line cannot use "classname", we have reserved that in the js, and perl uses class_name. so, we will use fm_class ........ r12182 | erickson | 2009-02-14 12:28:50 -0500 (Sat, 14 Feb 2009) | 1 line updated to match new fm_class field name ........ r12183 | scottmk | 2009-02-14 21:40:40 -0500 (Sat, 14 Feb 2009) | 6 lines 1. in searchJOIN(): fixed a bug in a couple of loops that search for links in the IDL. We were incrementing the index twice in each iteration, and thereby examining only ever other entry. 2. Constified a couple of character pointers. ........ r12184 | erickson | 2009-02-15 11:39:19 -0500 (Sun, 15 Feb 2009) | 1 line don't apply on-enter-equals-submit handler, cuz enter is conventiently used in various widgets to select values ........ r12185 | erickson | 2009-02-15 11:41:22 -0500 (Sun, 15 Feb 2009) | 1 line added store reset func ........ r12186 | erickson | 2009-02-15 11:58:57 -0500 (Sun, 15 Feb 2009) | 1 line linked in some more UI's ........ r12187 | dbs | 2009-02-15 13:53:56 -0500 (Sun, 15 Feb 2009) | 2 lines Cast LOWER arguments explicitly to text to deal with LOWER(id) - throws an exception on PostgreSQL 8.3 ........ r12189 | phasefx | 2009-02-15 17:30:44 -0500 (Sun, 15 Feb 2009) | 1 line grammar-o. Thanks Trinculo! ........ r12190 | dbs | 2009-02-15 21:14:28 -0500 (Sun, 15 Feb 2009) | 1 line Enable local administrators to run / view / share reports by default ........ r12191 | erickson | 2009-02-16 13:55:09 -0500 (Mon, 16 Feb 2009) | 1 line in addition to the field list, provide a name-based hash for faster/simpler runtime lookup ........ r12192 | erickson | 2009-02-16 16:55:52 -0500 (Mon, 16 Feb 2009) | 1 line plugged in perm group tree builder ........ r12193 | erickson | 2009-02-16 17:00:06 -0500 (Mon, 16 Feb 2009) | 1 line experimental flattened/idl-aware user editor that takes advantage of auto-widgets and field-docs (i.e. context help) ........ r12194 | erickson | 2009-02-16 21:30:46 -0500 (Mon, 16 Feb 2009) | 1 line initial plugin of staff-cats ........ r12195 | erickson | 2009-02-16 22:34:13 -0500 (Mon, 16 Feb 2009) | 1 line stat cat entries plugged in ........ r12196 | erickson | 2009-02-16 23:10:06 -0500 (Mon, 16 Feb 2009) | 1 line plugged in surveys ........ r12197 | dbs | 2009-02-16 23:59:13 -0500 (Mon, 16 Feb 2009) | 1 line en-US is better than no MARC editor tooltips at all; but we really need to HEAD the requested locale ........ r12200 | scottmk | 2009-02-17 09:03:35 -0500 (Tue, 17 Feb 2009) | 10 lines Opened a window into the innards of oils_cstore.c. 1. Gave external linkage to the SELECT function. 2. Added a new test_json_query.c, which calls the newly extern SELECT() to translate a JSON query into SQL. To be used for testing, debugging, and troubleshooting. Note that this utility calls SELECT() by normal linkage, not by dynamic loading. ........ r12201 | erickson | 2009-02-17 09:20:07 -0500 (Tue, 17 Feb 2009) | 1 line use colspan 0 to span the table length ........ r12202 | erickson | 2009-02-17 10:40:53 -0500 (Tue, 17 Feb 2009) | 1 line pcrud entries for ident_type ........ r12203 | erickson | 2009-02-17 11:12:46 -0500 (Tue, 17 Feb 2009) | 1 line plugged in saving of new users ........ r12204 | dbs | 2009-02-17 11:42:10 -0500 (Tue, 17 Feb 2009) | 2 lines Let's build test_json_query as part of autotools ........ r12205 | erickson | 2009-02-17 13:06:13 -0500 (Tue, 17 Feb 2009) | 1 line repaired org setting calls. added static version so no org-unit reference is required ........ r12206 | erickson | 2009-02-17 13:06:36 -0500 (Tue, 17 Feb 2009) | 1 line added batch org setting method ........ r12207 | erickson | 2009-02-17 13:06:57 -0500 (Tue, 17 Feb 2009) | 1 line explicitly load Util ........ r12208 | erickson | 2009-02-17 17:49:37 -0500 (Tue, 17 Feb 2009) | 1 line added int/float widgets ........ r12209 | erickson | 2009-02-17 17:50:41 -0500 (Tue, 17 Feb 2009) | 1 line plugged in new/delete addr handling ........ r12210 | erickson | 2009-02-17 17:57:37 -0500 (Tue, 17 Feb 2009) | 1 line allow for the setting of the page title from the sub-template ........ r12211 | scottmk | 2009-02-17 18:19:16 -0500 (Tue, 17 Feb 2009) | 3 lines In searchJOIN(): added a bit of sanity checking, to prevent segfaults induced by certain kinds of malformed JSOn queries. ........ r12213 | erickson | 2009-02-17 23:48:34 -0500 (Tue, 17 Feb 2009) | 1 line dojo-based interval2seconds ........ r12214 | erickson | 2009-02-17 23:49:20 -0500 (Tue, 17 Feb 2009) | 1 line added onchange to set expire date based on profile group selected ........ r12215 | erickson | 2009-02-17 23:52:25 -0500 (Tue, 17 Feb 2009) | 1 line initial dojo-ified xul glue ........ r12216 | dbs | 2009-02-17 23:55:26 -0500 (Tue, 17 Feb 2009) | 3 lines Avoid syntax errors due to mix of multi-valued INSERT clauses and single-valued INSERT clauses Avoid duplicate key error in that pesky permissions list ........ r12217 | scottmk | 2009-02-18 02:51:39 -0500 (Wed, 18 Feb 2009) | 4 lines searchJOIN() was segfaulting when the FROM clause was of the form "from": { "xxx":"yyy" }. Reason: we were freeing freeable_hash prematurely. Fixed. ........ r12218 | erickson | 2009-02-18 12:15:13 -0500 (Wed, 18 Feb 2009) | 1 line added onchange to copy new barcodes into usrname field ........ r12223 | scottmk | 2009-02-18 16:25:48 -0500 (Wed, 18 Feb 2009) | 3 lines Correct an error in the link specification for class asv. (Class aou doesn't have a "survey" field.) ........ r12227 | scottmk | 2009-02-18 21:12:46 -0500 (Wed, 18 Feb 2009) | 14 lines In searchJOIN: substantially rewrote the code for the condition where the JSON query specifies neither of the join columns. 1. Corrected an apparent bug whereby we could join in only one direction, even when a join in the opposite direction was valid and equivalent. 2. Rewrote the search loops for better performance. 3. When testing for the success of the searches for join columns, success should mean that we have identified both columns. Identifying just one isn't enough. ........ git-svn-id: svn://svn.open-ils.org/ILS/branches/staff-client-experiment@12228 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/examples/fm_IDL.xml | 376 +++++++++- Open-ILS/examples/opensrf.xml.example | 24 +- Open-ILS/examples/opensrf_core.xml.example | 2 + Open-ILS/src/Makefile.am | 8 + Open-ILS/src/c-apps/Makefile.am | 7 +- Open-ILS/src/c-apps/dump_idl.c | 2 + Open-ILS/src/c-apps/oils_cstore.c | 778 ++++++++++++--------- Open-ILS/src/c-apps/oils_idl-core.c | 9 + Open-ILS/src/c-apps/test_json_query.c | 261 +++++++ Open-ILS/src/extras/fast-extract | 1 + Open-ILS/src/extras/ils_events.xml | 3 + Open-ILS/src/extras/org_tree_html_options.pl | 9 +- Open-ILS/src/perlmods/OpenILS/Application/Actor.pm | 3 +- .../OpenILS/Application/Actor/Container.pm | 160 +++-- .../src/perlmods/OpenILS/Application/AppUtils.pm | 81 +-- .../perlmods/OpenILS/Application/Circ/Circulate.pm | 68 +- .../src/perlmods/OpenILS/Application/Circ/Holds.pm | 62 +- .../perlmods/OpenILS/Application/Circ/Survey.pm | 38 +- .../OpenILS/Application/Storage/CDBI/action.pm | 4 +- .../Application/Storage/Driver/Pg/storage.pm | 4 +- .../Application/Storage/Publisher/action.pm | 2 +- .../OpenILS/Application/Storage/Publisher/actor.pm | 8 +- .../src/perlmods/OpenILS/Application/Trigger.pm | 220 ++++++ .../perlmods/OpenILS/Application/Trigger/Event.pm | 111 ++- .../OpenILS/Application/Trigger/EventGroup.pm | 242 +++++++ Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm | 6 + .../src/perlmods/OpenILS/Utils/MFHD/Caption.pm | 214 ++++++ .../src/perlmods/OpenILS/Utils/MFHD/Holding.pm | 91 +-- Open-ILS/src/sql/Pg/002.schema.config.sql | 2 +- Open-ILS/src/sql/Pg/005.schema.actors.sql | 1 + Open-ILS/src/sql/Pg/012.schema.vandelay.sql | 4 +- Open-ILS/src/sql/Pg/040.schema.asset.sql | 8 +- Open-ILS/src/sql/Pg/070.schema.container.sql | 61 +- Open-ILS/src/sql/Pg/080.schema.money.sql | 2 +- Open-ILS/src/sql/Pg/090.schema.action.sql | 13 + Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql | 2 +- Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql | 2 +- Open-ILS/src/sql/Pg/200.schema.acq.sql | 118 ++-- Open-ILS/src/sql/Pg/210.schema.serials.sql | 2 +- Open-ILS/src/sql/Pg/400.schema.action_trigger.sql | 24 +- Open-ILS/src/sql/Pg/500.view.cross-schema.sql | 11 + Open-ILS/src/sql/Pg/950.data.seed-values.sql | 21 +- Open-ILS/src/sql/Pg/999.functions.global.sql | 4 +- Open-ILS/src/sql/Pg/extend-reporter.sql | 2 +- Open-ILS/tests/datasets/README | 1 + Open-ILS/tests/datasets/nepali.marc | 1 + Open-ILS/web/conify/global/admin.js | 4 +- Open-ILS/web/css/skin/default.css | 21 +- Open-ILS/web/js/dojo/fieldmapper/Fieldmapper.js | 3 +- Open-ILS/web/js/dojo/fieldmapper/IDL.js | 13 +- Open-ILS/web/js/dojo/fieldmapper/OrgUtils.js | 14 +- Open-ILS/web/js/dojo/openils/User.js | 1 + Open-ILS/web/js/dojo/openils/Util.js | 14 + Open-ILS/web/js/dojo/openils/XUL.js | 25 + Open-ILS/web/js/dojo/openils/acq/CurrencyType.js | 28 +- Open-ILS/web/js/dojo/openils/editors.js | 1 - .../web/js/dojo/openils/widget/AutoFieldWidget.js | 215 +++++- Open-ILS/web/js/dojo/openils/widget/AutoGrid.js | 175 ++++- Open-ILS/web/js/dojo/openils/widget/EditDialog.js | 14 +- Open-ILS/web/js/dojo/openils/widget/EditPane.js | 63 +- .../js/dojo/openils/widget/FilteringTreeSelect.js | 10 +- .../default/acq/financial/list_currency_types.js | 49 -- Open-ILS/web/js/ui/default/actor/user/register.js | 376 ++++++++++ .../js/ui/default/conify/global/acq/provider.js | 39 ++ .../default/conify/global/config/idl_field_doc.js | 50 ++ Open-ILS/web/opac/common/js/RemoteRequest.js | 3 +- Open-ILS/web/opac/images/Thumbs.db | Bin 0 -> 14847 bytes Open-ILS/web/opac/images/advancedsearch-icon.gif | Bin 0 -> 431 bytes Open-ILS/web/opac/images/advancedsearch-icon.png | Bin 0 -> 1137 bytes Open-ILS/web/opac/images/bg.gif | Bin 0 -> 287 bytes Open-ILS/web/opac/images/blank.gif | Bin 0 -> 43 bytes Open-ILS/web/opac/images/book-icon.png | Bin 0 -> 1538 bytes Open-ILS/web/opac/images/cancel-icon-u.gif | Bin 0 -> 332 bytes Open-ILS/web/opac/images/cancel-icon-u.png | Bin 0 -> 506 bytes Open-ILS/web/opac/images/cancel-icon.gif | Bin 0 -> 332 bytes Open-ILS/web/opac/images/cancel-icon.png | Bin 0 -> 507 bytes Open-ILS/web/opac/images/cartographic.jpg | Bin 0 -> 674 bytes Open-ILS/web/opac/images/chooselibrary-icon.gif | Bin 0 -> 547 bytes Open-ILS/web/opac/images/chooselibrary-icon.png | Bin 0 -> 1490 bytes Open-ILS/web/opac/images/closeall-icon-u.gif | Bin 0 -> 367 bytes Open-ILS/web/opac/images/closeall-icon-u.png | Bin 0 -> 552 bytes Open-ILS/web/opac/images/closeall-icon.gif | Bin 0 -> 367 bytes Open-ILS/web/opac/images/closeall-icon.png | Bin 0 -> 548 bytes Open-ILS/web/opac/images/content-bg.gif | Bin 0 -> 279 bytes Open-ILS/web/opac/images/content-bg.jpg | Bin 0 -> 368 bytes Open-ILS/web/opac/images/details-f-bg-u.gif | Bin 0 -> 261 bytes Open-ILS/web/opac/images/details-f-bg.gif | Bin 0 -> 261 bytes Open-ILS/web/opac/images/details-headers-bg.gif | Bin 0 -> 152 bytes Open-ILS/web/opac/images/earth-icon.png | Bin 0 -> 1206 bytes Open-ILS/web/opac/images/eg_tiny_logo.gif | Bin 0 -> 1164 bytes Open-ILS/web/opac/images/expandall-icon-u.gif | Bin 0 -> 380 bytes Open-ILS/web/opac/images/expandall-icon-u.png | Bin 0 -> 618 bytes Open-ILS/web/opac/images/expandall-icon.gif | Bin 0 -> 382 bytes Open-ILS/web/opac/images/expandall-icon.png | Bin 0 -> 607 bytes Open-ILS/web/opac/images/footer-bg.gif | Bin 0 -> 268 bytes Open-ILS/web/opac/images/footer-bl.gif | Bin 0 -> 100 bytes Open-ILS/web/opac/images/footer-bottom.gif | Bin 0 -> 50 bytes Open-ILS/web/opac/images/footer-br.gif | Bin 0 -> 100 bytes Open-ILS/web/opac/images/footer-corners.gif | Bin 0 -> 131 bytes Open-ILS/web/opac/images/footer-left.gif | Bin 0 -> 43 bytes Open-ILS/web/opac/images/footer-right.gif | Bin 0 -> 43 bytes Open-ILS/web/opac/images/footer-tl.gif | Bin 0 -> 99 bytes Open-ILS/web/opac/images/footer-top.gif | Bin 0 -> 50 bytes Open-ILS/web/opac/images/footer-tr.gif | Bin 0 -> 99 bytes Open-ILS/web/opac/images/header-bg.gif | Bin 0 -> 510 bytes Open-ILS/web/opac/images/header-shadow.gif | Bin 0 -> 63 bytes Open-ILS/web/opac/images/home-bottom-tag-bg.gif | Bin 0 -> 169 bytes Open-ILS/web/opac/images/home-icon-u.gif | Bin 0 -> 592 bytes Open-ILS/web/opac/images/home-icon-u.png | Bin 0 -> 1605 bytes Open-ILS/web/opac/images/home-icon.gif | Bin 0 -> 596 bytes Open-ILS/web/opac/images/home-icon.png | Bin 0 -> 1594 bytes Open-ILS/web/opac/images/inner-account-icon-u.gif | Bin 0 -> 468 bytes Open-ILS/web/opac/images/inner-account-icon-u.png | Bin 0 -> 1210 bytes Open-ILS/web/opac/images/inner-account-icon.gif | Bin 0 -> 465 bytes Open-ILS/web/opac/images/inner-account-icon.png | Bin 0 -> 1181 bytes Open-ILS/web/opac/images/inner-advanced-icon-u.gif | Bin 0 -> 431 bytes Open-ILS/web/opac/images/inner-advanced-icon-u.png | Bin 0 -> 1137 bytes Open-ILS/web/opac/images/inner-advanced-icon.gif | Bin 0 -> 431 bytes Open-ILS/web/opac/images/inner-advanced-icon.png | Bin 0 -> 1114 bytes Open-ILS/web/opac/images/lg-txt.gif | Bin 0 -> 826 bytes Open-ILS/web/opac/images/libselect-btn.gif | Bin 0 -> 2970 bytes Open-ILS/web/opac/images/list-icon.gif | Bin 0 -> 128 bytes Open-ILS/web/opac/images/list-icon.png | Bin 0 -> 272 bytes Open-ILS/web/opac/images/login-icon-u.gif | Bin 0 -> 462 bytes Open-ILS/web/opac/images/login-icon-u.png | Bin 0 -> 1332 bytes Open-ILS/web/opac/images/login-icon.gif | Bin 0 -> 462 bytes Open-ILS/web/opac/images/login-icon.png | Bin 0 -> 1332 bytes Open-ILS/web/opac/images/loginas-icon.gif | Bin 0 -> 431 bytes Open-ILS/web/opac/images/loginas-icon.png | Bin 0 -> 966 bytes Open-ILS/web/opac/images/logo.gif | Bin 0 -> 2250 bytes Open-ILS/web/opac/images/logo.png | Bin 0 -> 4421 bytes Open-ILS/web/opac/images/logout-icon-u.gif | Bin 0 -> 420 bytes Open-ILS/web/opac/images/logout-icon-u.png | Bin 0 -> 858 bytes Open-ILS/web/opac/images/logout-icon.gif | Bin 0 -> 418 bytes Open-ILS/web/opac/images/logout-icon.png | Bin 0 -> 830 bytes Open-ILS/web/opac/images/mix-icon.png | Bin 0 -> 1591 bytes Open-ILS/web/opac/images/mixed material.jpg | Bin 0 -> 784 bytes Open-ILS/web/opac/images/mov-icon.png | Bin 0 -> 1532 bytes Open-ILS/web/opac/images/moving image.jpg | Bin 0 -> 745 bytes Open-ILS/web/opac/images/mussymbol-icon.png | Bin 0 -> 1029 bytes Open-ILS/web/opac/images/myaccount-icon.gif | Bin 0 -> 468 bytes Open-ILS/web/opac/images/myaccount-icon.png | Bin 0 -> 1210 bytes Open-ILS/web/opac/images/noimg.gif | Bin 0 -> 385 bytes Open-ILS/web/opac/images/nonmusic-icon.png | Bin 0 -> 1217 bytes Open-ILS/web/opac/images/notated music.jpg | Bin 0 -> 601 bytes Open-ILS/web/opac/images/pic-icon.png | Bin 0 -> 1849 bytes Open-ILS/web/opac/images/placeholder-icon.png | Bin 0 -> 891 bytes Open-ILS/web/opac/images/progressbar_green-old.gif | Bin 0 -> 1522 bytes Open-ILS/web/opac/images/progressbar_green.gif | Bin 1522 -> 2767 bytes Open-ILS/web/opac/images/recsound-icon.png | Bin 0 -> 1512 bytes Open-ILS/web/opac/images/reg-txt.gif | Bin 0 -> 827 bytes Open-ILS/web/opac/images/relevant-icon-u.gif | Bin 0 -> 532 bytes Open-ILS/web/opac/images/relevant-icon-u.png | Bin 0 -> 1383 bytes Open-ILS/web/opac/images/relevant-icon.gif | Bin 0 -> 535 bytes Open-ILS/web/opac/images/relevant-icon.png | Bin 0 -> 1349 bytes Open-ILS/web/opac/images/search-btn.gif | Bin 0 -> 1463 bytes Open-ILS/web/opac/images/searchbar-bg.gif | Bin 0 -> 2610 bytes Open-ILS/web/opac/images/searchbox-bg.gif | Bin 0 -> 270 bytes Open-ILS/web/opac/images/series-icon-u.gif | Bin 0 -> 532 bytes Open-ILS/web/opac/images/series-icon-u.png | Bin 0 -> 1004 bytes Open-ILS/web/opac/images/series-icon.gif | Bin 0 -> 532 bytes Open-ILS/web/opac/images/series-icon.png | Bin 0 -> 1004 bytes Open-ILS/web/opac/images/sidebar-bg.gif | Bin 0 -> 98 bytes Open-ILS/web/opac/images/slimtree/folder2.gif | Bin 425 -> 293 bytes Open-ILS/web/opac/images/slimtree/folderopen2.gif | Bin 433 -> 309 bytes Open-ILS/web/opac/images/small-rss.gif | Bin 0 -> 399 bytes Open-ILS/web/opac/images/software, multimedia.jpg | Bin 0 -> 650 bytes Open-ILS/web/opac/images/software-icon.png | Bin 0 -> 955 bytes .../web/opac/images/sound recording-musical.jpg | Bin 0 -> 773 bytes .../web/opac/images/sound recording-nonmusical.jpg | Bin 0 -> 651 bytes Open-ILS/web/opac/images/sound recording.jpg | Bin 0 -> 733 bytes Open-ILS/web/opac/images/sound-icon.png | Bin 0 -> 1440 bytes Open-ILS/web/opac/images/still images.jpg | Bin 0 -> 287 bytes Open-ILS/web/opac/images/subject-icon-u.gif | Bin 0 -> 477 bytes Open-ILS/web/opac/images/subject-icon-u.png | Bin 0 -> 1158 bytes Open-ILS/web/opac/images/text.jpg | Bin 0 -> 773 bytes .../web/opac/images/three dimensional object.jpg | Bin 0 -> 694 bytes Open-ILS/web/opac/images/threed-icon.png | Bin 0 -> 1339 bytes Open-ILS/web/opac/images/title-icon-u.gif | Bin 0 -> 554 bytes Open-ILS/web/opac/images/title-icon-u.png | Bin 0 -> 1106 bytes Open-ILS/web/opac/images/title-icon.gif | Bin 0 -> 536 bytes Open-ILS/web/opac/images/title-icon.png | Bin 0 -> 1092 bytes Open-ILS/web/opac/images/titledetails-icon-u.gif | Bin 0 -> 486 bytes Open-ILS/web/opac/images/titledetails-icon-u.png | Bin 0 -> 771 bytes Open-ILS/web/opac/images/titledetails-icon.gif | Bin 0 -> 472 bytes Open-ILS/web/opac/images/titledetails-icon.png | Bin 0 -> 760 bytes Open-ILS/web/opac/images/tor/book-icon.gif | Bin 0 -> 544 bytes Open-ILS/web/opac/images/tor/book-icon.png | Bin 0 -> 1538 bytes Open-ILS/web/opac/images/tor/cartographic.jpg | Bin 690 -> 674 bytes Open-ILS/web/opac/images/tor/earth-icon.gif | Bin 0 -> 509 bytes Open-ILS/web/opac/images/tor/earth-icon.png | Bin 0 -> 1206 bytes Open-ILS/web/opac/images/tor/mix-icon.gif | Bin 0 -> 545 bytes Open-ILS/web/opac/images/tor/mix-icon.png | Bin 0 -> 1591 bytes Open-ILS/web/opac/images/tor/mixed material.jpg | Bin 525 -> 784 bytes Open-ILS/web/opac/images/tor/mov-icon.gif | Bin 0 -> 592 bytes Open-ILS/web/opac/images/tor/mov-icon.png | Bin 0 -> 1532 bytes Open-ILS/web/opac/images/tor/moving image.jpg | Bin 540 -> 745 bytes Open-ILS/web/opac/images/tor/mussymbol-icon.gif | Bin 0 -> 460 bytes Open-ILS/web/opac/images/tor/mussymbol-icon.png | Bin 0 -> 1029 bytes Open-ILS/web/opac/images/tor/nonmusic-icon.gif | Bin 0 -> 490 bytes Open-ILS/web/opac/images/tor/nonmusic-icon.png | Bin 0 -> 1217 bytes Open-ILS/web/opac/images/tor/notated music.jpg | Bin 1826 -> 601 bytes Open-ILS/web/opac/images/tor/pic-icon.gif | Bin 0 -> 604 bytes Open-ILS/web/opac/images/tor/pic-icon.png | Bin 0 -> 1849 bytes Open-ILS/web/opac/images/tor/placeholder-icon.gif | Bin 0 -> 424 bytes Open-ILS/web/opac/images/tor/placeholder-icon.png | Bin 0 -> 891 bytes Open-ILS/web/opac/images/tor/recsound-icon.gif | Bin 0 -> 634 bytes Open-ILS/web/opac/images/tor/recsound-icon.png | Bin 0 -> 1512 bytes .../web/opac/images/tor/software, multimedia.jpg | Bin 442 -> 650 bytes Open-ILS/web/opac/images/tor/software-icon.gif | Bin 0 -> 471 bytes Open-ILS/web/opac/images/tor/software-icon.png | Bin 0 -> 955 bytes .../opac/images/tor/sound recording-musical.jpg | Bin 618 -> 773 bytes .../opac/images/tor/sound recording-nonmusical.jpg | Bin 618 -> 651 bytes Open-ILS/web/opac/images/tor/sound recording.jpg | Bin 618 -> 733 bytes Open-ILS/web/opac/images/tor/sound-icon.gif | Bin 0 -> 598 bytes Open-ILS/web/opac/images/tor/sound-icon.png | Bin 0 -> 1440 bytes Open-ILS/web/opac/images/tor/still images.jpg | Bin 376 -> 287 bytes Open-ILS/web/opac/images/tor/text.jpg | Bin 525 -> 773 bytes .../opac/images/tor/three dimensional object.jpg | Bin 1964 -> 694 bytes Open-ILS/web/opac/images/tor/threed-icon.gif | Bin 0 -> 526 bytes Open-ILS/web/opac/images/tor/threed-icon.png | Bin 0 -> 1339 bytes Open-ILS/web/opac/skin/craftsman/css/default.css | 269 +++++++ Open-ILS/web/opac/skin/craftsman/css/layout.css | 246 +++++++ .../skin/craftsman/xml/advanced/adv_global_row.xml | 36 + .../craftsman/xml/advanced/advanced_global.xml | 287 ++++++++ Open-ILS/web/opac/skin/craftsman/xml/body.xml | 51 ++ .../opac/skin/craftsman/xml/common/altcanvas.xml | 37 + .../opac/skin/craftsman/xml/common/cn_browse.xml | 36 + .../opac/skin/craftsman/xml/common/css_common.xml | 26 + .../web/opac/skin/craftsman/xml/common/fonts.xml | 28 + .../web/opac/skin/craftsman/xml/common/holds.xml | 242 +++++++ .../opac/skin/craftsman/xml/common/libselect.xml | 15 + .../web/opac/skin/craftsman/xml/common/login.xml | 107 +++ .../web/opac/skin/craftsman/xml/common/logo.xml | 2 + .../web/opac/skin/craftsman/xml/common/orgtree.xml | 23 + .../opac/skin/craftsman/xml/common/searchbar.xml | 34 + .../web/opac/skin/craftsman/xml/common/sidebar.xml | 174 +++++ .../opac/skin/craftsman/xml/common/statusbar.xml | 24 + .../web/opac/skin/craftsman/xml/common/tips.xml | 8 + Open-ILS/web/opac/skin/craftsman/xml/footer.xml | 23 + .../opac/skin/craftsman/xml/home/homesearch.xml | 92 +++ .../opac/skin/craftsman/xml/home/index_body.xml | 28 + .../web/opac/skin/craftsman/xml/page_cnbrowse.xml | 6 + .../web/opac/skin/craftsman/xml/page_myopac.xml | 68 ++ .../web/opac/skin/craftsman/xml/page_rdetail.xml | 85 +++ .../craftsman/xml/rdetail/rdetail_cn_details.xml | 99 +++ .../craftsman/xml/rdetail/rdetail_copyinfo.xml | 73 ++ .../skin/craftsman/xml/rdetail/rdetail_extras.xml | 78 +++ .../skin/craftsman/xml/rdetail/rdetail_summary.xml | 83 +++ .../opac/skin/craftsman/xml/result/filtersort.xml | 25 + .../opac/skin/craftsman/xml/result/result_info.xml | 74 ++ .../skin/craftsman/xml/result/result_lowhits.xml | 39 ++ .../skin/craftsman/xml/result/result_table.xml | 146 ++++ Open-ILS/web/opac/skin/default/js/holds.js | 2 +- Open-ILS/web/opac/skin/default/js/myopac.js | 1 + .../web/opac/skin/default/xml/common/js_common.xml | 2 +- Open-ILS/web/opac/theme/craftsman/css/colors.css | 100 +++ Open-ILS/web/templates/base.tt2 | 3 +- .../default/acq/financial/list_currency_types.tt2 | 52 -- .../web/templates/default/actor/user/register.tt2 | 141 ++++ .../default/conify/global/acq/currency_type.tt2 | 26 + .../default/conify/global/acq/exchange_rate.tt2 | 26 + .../default/conify/global/acq/provider.tt2 | 30 + .../global/action_trigger/event_definition.tt2 | 114 +++ .../default/conify/global/config/idl_field_doc.tt2 | 29 + Open-ILS/web/templates/default/menu.tt2 | 49 +- .../staff_client/chrome/content/main/about.html | 4 +- Open-ILS/xul/staff_client/server/cat/marcedit.js | 22 +- .../xul/staff_client/server/patron/ue_config.js | 3 +- 269 files changed, 6521 insertions(+), 838 deletions(-) create mode 100644 Open-ILS/src/c-apps/test_json_query.c create mode 100644 Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm create mode 100644 Open-ILS/tests/datasets/nepali.marc create mode 100644 Open-ILS/web/js/dojo/openils/XUL.js delete mode 100644 Open-ILS/web/js/ui/default/acq/financial/list_currency_types.js create mode 100644 Open-ILS/web/js/ui/default/actor/user/register.js create mode 100644 Open-ILS/web/js/ui/default/conify/global/acq/provider.js create mode 100644 Open-ILS/web/js/ui/default/conify/global/config/idl_field_doc.js create mode 100644 Open-ILS/web/opac/images/Thumbs.db create mode 100644 Open-ILS/web/opac/images/advancedsearch-icon.gif create mode 100644 Open-ILS/web/opac/images/advancedsearch-icon.png create mode 100644 Open-ILS/web/opac/images/bg.gif create mode 100644 Open-ILS/web/opac/images/blank.gif create mode 100644 Open-ILS/web/opac/images/book-icon.png create mode 100644 Open-ILS/web/opac/images/cancel-icon-u.gif create mode 100644 Open-ILS/web/opac/images/cancel-icon-u.png create mode 100644 Open-ILS/web/opac/images/cancel-icon.gif create mode 100644 Open-ILS/web/opac/images/cancel-icon.png create mode 100644 Open-ILS/web/opac/images/cartographic.jpg create mode 100644 Open-ILS/web/opac/images/chooselibrary-icon.gif create mode 100644 Open-ILS/web/opac/images/chooselibrary-icon.png create mode 100644 Open-ILS/web/opac/images/closeall-icon-u.gif create mode 100644 Open-ILS/web/opac/images/closeall-icon-u.png create mode 100644 Open-ILS/web/opac/images/closeall-icon.gif create mode 100644 Open-ILS/web/opac/images/closeall-icon.png create mode 100644 Open-ILS/web/opac/images/content-bg.gif create mode 100644 Open-ILS/web/opac/images/content-bg.jpg create mode 100644 Open-ILS/web/opac/images/details-f-bg-u.gif create mode 100644 Open-ILS/web/opac/images/details-f-bg.gif create mode 100644 Open-ILS/web/opac/images/details-headers-bg.gif create mode 100644 Open-ILS/web/opac/images/earth-icon.png create mode 100644 Open-ILS/web/opac/images/eg_tiny_logo.gif create mode 100644 Open-ILS/web/opac/images/expandall-icon-u.gif create mode 100644 Open-ILS/web/opac/images/expandall-icon-u.png create mode 100644 Open-ILS/web/opac/images/expandall-icon.gif create mode 100644 Open-ILS/web/opac/images/expandall-icon.png create mode 100644 Open-ILS/web/opac/images/footer-bg.gif create mode 100644 Open-ILS/web/opac/images/footer-bl.gif create mode 100644 Open-ILS/web/opac/images/footer-bottom.gif create mode 100644 Open-ILS/web/opac/images/footer-br.gif create mode 100644 Open-ILS/web/opac/images/footer-corners.gif create mode 100644 Open-ILS/web/opac/images/footer-left.gif create mode 100644 Open-ILS/web/opac/images/footer-right.gif create mode 100644 Open-ILS/web/opac/images/footer-tl.gif create mode 100644 Open-ILS/web/opac/images/footer-top.gif create mode 100644 Open-ILS/web/opac/images/footer-tr.gif create mode 100644 Open-ILS/web/opac/images/header-bg.gif create mode 100644 Open-ILS/web/opac/images/header-shadow.gif create mode 100644 Open-ILS/web/opac/images/home-bottom-tag-bg.gif create mode 100644 Open-ILS/web/opac/images/home-icon-u.gif create mode 100644 Open-ILS/web/opac/images/home-icon-u.png create mode 100644 Open-ILS/web/opac/images/home-icon.gif create mode 100644 Open-ILS/web/opac/images/home-icon.png create mode 100644 Open-ILS/web/opac/images/inner-account-icon-u.gif create mode 100644 Open-ILS/web/opac/images/inner-account-icon-u.png create mode 100644 Open-ILS/web/opac/images/inner-account-icon.gif create mode 100644 Open-ILS/web/opac/images/inner-account-icon.png create mode 100644 Open-ILS/web/opac/images/inner-advanced-icon-u.gif create mode 100644 Open-ILS/web/opac/images/inner-advanced-icon-u.png create mode 100644 Open-ILS/web/opac/images/inner-advanced-icon.gif create mode 100644 Open-ILS/web/opac/images/inner-advanced-icon.png create mode 100644 Open-ILS/web/opac/images/lg-txt.gif create mode 100644 Open-ILS/web/opac/images/libselect-btn.gif create mode 100644 Open-ILS/web/opac/images/list-icon.gif create mode 100644 Open-ILS/web/opac/images/list-icon.png create mode 100644 Open-ILS/web/opac/images/login-icon-u.gif create mode 100644 Open-ILS/web/opac/images/login-icon-u.png create mode 100644 Open-ILS/web/opac/images/login-icon.gif create mode 100644 Open-ILS/web/opac/images/login-icon.png create mode 100644 Open-ILS/web/opac/images/loginas-icon.gif create mode 100644 Open-ILS/web/opac/images/loginas-icon.png create mode 100644 Open-ILS/web/opac/images/logo.gif create mode 100644 Open-ILS/web/opac/images/logo.png create mode 100644 Open-ILS/web/opac/images/logout-icon-u.gif create mode 100644 Open-ILS/web/opac/images/logout-icon-u.png create mode 100644 Open-ILS/web/opac/images/logout-icon.gif create mode 100644 Open-ILS/web/opac/images/logout-icon.png create mode 100644 Open-ILS/web/opac/images/mix-icon.png create mode 100644 Open-ILS/web/opac/images/mixed material.jpg create mode 100644 Open-ILS/web/opac/images/mov-icon.png create mode 100644 Open-ILS/web/opac/images/moving image.jpg create mode 100644 Open-ILS/web/opac/images/mussymbol-icon.png create mode 100644 Open-ILS/web/opac/images/myaccount-icon.gif create mode 100644 Open-ILS/web/opac/images/myaccount-icon.png create mode 100644 Open-ILS/web/opac/images/noimg.gif create mode 100644 Open-ILS/web/opac/images/nonmusic-icon.png create mode 100644 Open-ILS/web/opac/images/notated music.jpg create mode 100644 Open-ILS/web/opac/images/pic-icon.png create mode 100644 Open-ILS/web/opac/images/placeholder-icon.png create mode 100644 Open-ILS/web/opac/images/progressbar_green-old.gif create mode 100644 Open-ILS/web/opac/images/recsound-icon.png create mode 100644 Open-ILS/web/opac/images/reg-txt.gif create mode 100644 Open-ILS/web/opac/images/relevant-icon-u.gif create mode 100644 Open-ILS/web/opac/images/relevant-icon-u.png create mode 100644 Open-ILS/web/opac/images/relevant-icon.gif create mode 100644 Open-ILS/web/opac/images/relevant-icon.png create mode 100644 Open-ILS/web/opac/images/search-btn.gif create mode 100644 Open-ILS/web/opac/images/searchbar-bg.gif create mode 100644 Open-ILS/web/opac/images/searchbox-bg.gif create mode 100644 Open-ILS/web/opac/images/series-icon-u.gif create mode 100644 Open-ILS/web/opac/images/series-icon-u.png create mode 100644 Open-ILS/web/opac/images/series-icon.gif create mode 100644 Open-ILS/web/opac/images/series-icon.png create mode 100644 Open-ILS/web/opac/images/sidebar-bg.gif create mode 100644 Open-ILS/web/opac/images/small-rss.gif create mode 100644 Open-ILS/web/opac/images/software, multimedia.jpg create mode 100644 Open-ILS/web/opac/images/software-icon.png create mode 100644 Open-ILS/web/opac/images/sound recording-musical.jpg create mode 100644 Open-ILS/web/opac/images/sound recording-nonmusical.jpg create mode 100644 Open-ILS/web/opac/images/sound recording.jpg create mode 100644 Open-ILS/web/opac/images/sound-icon.png create mode 100644 Open-ILS/web/opac/images/still images.jpg create mode 100644 Open-ILS/web/opac/images/subject-icon-u.gif create mode 100644 Open-ILS/web/opac/images/subject-icon-u.png create mode 100644 Open-ILS/web/opac/images/text.jpg create mode 100644 Open-ILS/web/opac/images/three dimensional object.jpg create mode 100644 Open-ILS/web/opac/images/threed-icon.png create mode 100644 Open-ILS/web/opac/images/title-icon-u.gif create mode 100644 Open-ILS/web/opac/images/title-icon-u.png create mode 100644 Open-ILS/web/opac/images/title-icon.gif create mode 100644 Open-ILS/web/opac/images/title-icon.png create mode 100644 Open-ILS/web/opac/images/titledetails-icon-u.gif create mode 100644 Open-ILS/web/opac/images/titledetails-icon-u.png create mode 100644 Open-ILS/web/opac/images/titledetails-icon.gif create mode 100644 Open-ILS/web/opac/images/titledetails-icon.png create mode 100644 Open-ILS/web/opac/images/tor/book-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/book-icon.png create mode 100644 Open-ILS/web/opac/images/tor/earth-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/earth-icon.png create mode 100644 Open-ILS/web/opac/images/tor/mix-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/mix-icon.png create mode 100644 Open-ILS/web/opac/images/tor/mov-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/mov-icon.png create mode 100644 Open-ILS/web/opac/images/tor/mussymbol-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/mussymbol-icon.png create mode 100644 Open-ILS/web/opac/images/tor/nonmusic-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/nonmusic-icon.png create mode 100644 Open-ILS/web/opac/images/tor/pic-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/pic-icon.png create mode 100644 Open-ILS/web/opac/images/tor/placeholder-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/placeholder-icon.png create mode 100644 Open-ILS/web/opac/images/tor/recsound-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/recsound-icon.png create mode 100644 Open-ILS/web/opac/images/tor/software-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/software-icon.png create mode 100644 Open-ILS/web/opac/images/tor/sound-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/sound-icon.png create mode 100644 Open-ILS/web/opac/images/tor/threed-icon.gif create mode 100644 Open-ILS/web/opac/images/tor/threed-icon.png create mode 100644 Open-ILS/web/opac/skin/craftsman/css/default.css create mode 100644 Open-ILS/web/opac/skin/craftsman/css/layout.css create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/advanced/adv_global_row.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/advanced/advanced_global.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/body.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/altcanvas.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/cn_browse.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/css_common.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/fonts.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/holds.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/libselect.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/login.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/logo.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/orgtree.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/searchbar.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/sidebar.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/statusbar.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/common/tips.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/footer.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/home/homesearch.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/home/index_body.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/page_cnbrowse.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/page_myopac.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/page_rdetail.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/rdetail/rdetail_cn_details.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/rdetail/rdetail_copyinfo.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/rdetail/rdetail_extras.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/rdetail/rdetail_summary.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/result/filtersort.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/result/result_info.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/result/result_lowhits.xml create mode 100644 Open-ILS/web/opac/skin/craftsman/xml/result/result_table.xml create mode 100644 Open-ILS/web/opac/theme/craftsman/css/colors.css delete mode 100644 Open-ILS/web/templates/default/acq/financial/list_currency_types.tt2 create mode 100644 Open-ILS/web/templates/default/actor/user/register.tt2 create mode 100644 Open-ILS/web/templates/default/conify/global/acq/currency_type.tt2 create mode 100644 Open-ILS/web/templates/default/conify/global/acq/exchange_rate.tt2 create mode 100644 Open-ILS/web/templates/default/conify/global/acq/provider.tt2 create mode 100644 Open-ILS/web/templates/default/conify/global/action_trigger/event_definition.tt2 create mode 100644 Open-ILS/web/templates/default/conify/global/config/idl_field_doc.tt2 diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index e90a6c1a7a..37285eb0de 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -580,7 +580,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -591,6 +591,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + @@ -604,7 +612,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -613,9 +621,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + - + @@ -624,9 +640,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + - + @@ -635,6 +659,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + @@ -654,7 +686,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -676,14 +708,22 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - - - - + + + + + + + + + + + + + @@ -956,7 +996,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -965,6 +1005,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + @@ -998,7 +1046,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -1181,10 +1229,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + @@ -1964,7 +2028,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -1984,7 +2048,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -2030,6 +2094,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + @@ -2292,10 +2357,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + @@ -2316,6 +2397,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + @@ -2537,7 +2631,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -2614,6 +2708,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + @@ -2628,6 +2724,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + @@ -2702,6 +2799,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + @@ -2781,6 +2891,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + @@ -2995,10 +3118,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + @@ -3295,7 +3434,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -3446,6 +3585,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + @@ -3616,10 +3768,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + @@ -3637,6 +3805,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + + + + + + @@ -3732,6 +3921,29 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + + + + + + + + @@ -3752,7 +3964,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -3779,7 +3991,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -3793,7 +4005,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -3807,9 +4019,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + - + @@ -3824,6 +4044,122 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/examples/opensrf.xml.example b/Open-ILS/examples/opensrf.xml.example index 8600288215..ea485d2de9 100644 --- a/Open-ILS/examples/opensrf.xml.example +++ b/Open-ILS/examples/opensrf.xml.example @@ -252,6 +252,25 @@ vim:et:ts=4:sw=4: + + + 5 + 1 + perl + OpenILS::Application::Acq + 100 + + open-ils.acq_unix.sock + open-ils.acq_unix.pid + open-ils.acq_unix.log + 100 + 1 + 15 + 1 + 5 + + + @@ -327,7 +346,7 @@ vim:et:ts=4:sw=4: oilsMARC21slim2HTML.xsl - oilsMARC21slim2HTMLslim.xsl + oilsMARC21slim2HTMLslim.xsl true @@ -832,7 +851,7 @@ vim:et:ts=4:sw=4: 5 1 perl - OpenILS::Application::Vandelay + OpenILS::Application::Vandelay 100 vandelay_unix.sock @@ -875,6 +894,7 @@ vim:et:ts=4:sw=4: opensrf.settings opensrf.math opensrf.dbmath + open-ils.acq open-ils.cat open-ils.supercat open-ils.search diff --git a/Open-ILS/examples/opensrf_core.xml.example b/Open-ILS/examples/opensrf_core.xml.example index 889e525452..bc7b929783 100644 --- a/Open-ILS/examples/opensrf_core.xml.example +++ b/Open-ILS/examples/opensrf_core.xml.example @@ -20,11 +20,13 @@ Example OpenSRF bootstrap configuration file for Evergreen opensrf.math open-ils.actor + open-ils.acq open-ils.auth open-ils.cat open-ils.circ open-ils.collections open-ils.fielder + open-ils.pcrud open-ils.permacrud open-ils.reporter open-ils.search diff --git a/Open-ILS/src/Makefile.am b/Open-ILS/src/Makefile.am index 556016a417..4d53679846 100644 --- a/Open-ILS/src/Makefile.am +++ b/Open-ILS/src/Makefile.am @@ -243,12 +243,20 @@ webcore-install: cp -r @top_srcdir@/Open-ILS/web/. $(DESTDIR)$(WEBDIR) cp @top_srcdir@/Open-ILS/xsl/*.xsl $(opacextrasdir) cp @top_srcdir@/Open-ILS/xsl/*.xsl $(XSLDIR) + cp -r $(DESTDIR)$(WEBDIR)/opac/skin/default/* $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/ + cp -r @top_srcdir@/Open-ILS/web/opac/skin/craftsman/* $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/ ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/mresult.xml ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/rresult.xml ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/rdetail.xml ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/advanced.xml ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/myopac.xml ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/default/xml/cnbrowse.xml + ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/mresult.xml + ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/rresult.xml + ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/rdetail.xml + ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/advanced.xml + ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/myopac.xml + ln -sf $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/index.xml $(DESTDIR)$(WEBDIR)/opac/skin/craftsman/xml/cnbrowse.xml offline-install: diff --git a/Open-ILS/src/c-apps/Makefile.am b/Open-ILS/src/c-apps/Makefile.am index b8e6d1c50e..11f4197c6e 100644 --- a/Open-ILS/src/c-apps/Makefile.am +++ b/Open-ILS/src/c-apps/Makefile.am @@ -7,7 +7,7 @@ AM_CFLAGS = $(DEF_CFLAGS) -DOSRF_LOG_PARAMS -I@top_srcdir@/include/ AM_LDFLAGS = $(DEF_LDFLAGS) -L$(DBI_LIBS) -lopensrf -bin_PROGRAMS = oils_dataloader dump_idl +bin_PROGRAMS = oils_dataloader dump_idl test_json_query oils_dataloader_SOURCES = oils_dataloader.c oils_dataloader_LDFLAGS = $(AM_LDFLAGS) -loils_idl oils_dataloader_DEPENDENCIES = liboils_idl.la liboils_utils.la @@ -16,6 +16,11 @@ dump_idl_SOURCES = dump_idl.c dump_idl_LDFLAGS = $(AM_LDFLAGS) -loils_idl dump_idl_DEPENDENCIES = liboils_idl.la liboils_utils.la +test_json_query_SOURCES = test_json_query.c +test_json_query_LDFLAGS = $(AM_LDFLAGS) -loils_idl +test_json_query_LDADD = oils_cstore.la +test_json_query_DEPENDENCIES = liboils_idl.la liboils_utils.la + lib_LTLIBRARIES = liboils_idl.la liboils_utils.la oils_cstore.la oils_rstore.la oils_pcrud.la oils_auth.la liboils_idl_la_SOURCES = oils_idl-core.c diff --git a/Open-ILS/src/c-apps/dump_idl.c b/Open-ILS/src/c-apps/dump_idl.c index 3c8c36017e..efeee4503c 100644 --- a/Open-ILS/src/c-apps/dump_idl.c +++ b/Open-ILS/src/c-apps/dump_idl.c @@ -141,6 +141,8 @@ static void dump_class( osrfHash* class_hash, const char* class_name ) printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr ); else if( !strcmp( attr_name, "tablename" ) ) printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr ); + else if( !strcmp( attr_name, "restrict_primary" ) ) + printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr ); else if( !strcmp( attr_name, "virtual" ) ) printf( "%s%s: %s\n", indent, attr_name, (char*) class_attr ); else if( !strcmp( attr_name, "controller" ) ) diff --git a/Open-ILS/src/c-apps/oils_cstore.c b/Open-ILS/src/c-apps/oils_cstore.c index c93da144af..59559fa28b 100644 --- a/Open-ILS/src/c-apps/oils_cstore.c +++ b/Open-ILS/src/c-apps/oils_cstore.c @@ -61,17 +61,20 @@ static char* searchFunctionPredicate ( const char*, osrfHash*, const jsonObject* static char* searchFieldTransform ( const char*, osrfHash*, const jsonObject*); static char* searchFieldTransformPredicate ( const char*, osrfHash*, jsonObject*, const char* ); static char* searchBETWEENPredicate ( const char*, osrfHash*, jsonObject* ); -static char* searchINPredicate ( const char*, osrfHash*, const jsonObject*, const char* ); -static char* searchPredicate ( const char*, osrfHash*, jsonObject* ); +static char* searchINPredicate ( const char*, osrfHash*, + jsonObject*, const char*, osrfMethodContext* ); +static char* searchPredicate ( const char*, osrfHash*, jsonObject*, osrfMethodContext* ); static char* searchJOIN ( const jsonObject*, osrfHash* ); static char* searchWHERE ( const jsonObject*, osrfHash*, int, osrfMethodContext* ); static char* buildSELECT ( jsonObject*, jsonObject*, osrfHash*, osrfMethodContext* ); -static char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int ); +char* SELECT ( osrfMethodContext*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, jsonObject*, int ); void userDataFree( void* ); static void sessionDataFree( char*, void* ); static char* getSourceDefinition( osrfHash* ); +static int str_is_true( const char* str ); +static int obj_is_true( const jsonObject* obj ); #ifdef PCRUD static jsonObject* verifyUserPCRUD( osrfMethodContext* ); @@ -186,11 +189,10 @@ int osrfAppInitialize() { continue; } - char* virt = osrfHashGet(idlClass, "virtual"); - if (virt && !strcmp( virt, "true")) { - osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname ); - continue; - } + if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) { + osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname ); + continue; + } // Look up some other attributes of the current class const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper"); @@ -217,8 +219,7 @@ int osrfAppInitialize() { if (!osrfHashGet( idlClass_permacrud, tmp_method )) continue; #endif - if ( readonly && - !strncasecmp( "true", readonly, 4) && + if ( str_is_true( readonly ) && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') ) continue; @@ -357,11 +358,10 @@ int osrfAppChildInit() { osrfHash* class = osrfHashGet( oilsIDL(), classname ); osrfHash* fields = osrfHashGet( class, "fields" ); - char* virt = osrfHashGet(class, "virtual"); - if (virt && !strcmp( virt, "true")) { - osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname ); - continue; - } + if( str_is_true( osrfHashGet(class, "virtual") ) ) { + osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname ); + continue; + } char* tabledef = getSourceDefinition(class); @@ -470,7 +470,10 @@ static void sessionDataFree( char* key, void* item ) { } int beginTransaction ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } #ifdef PCRUD jsonObject* user = verifyUserPCRUD( ctx ); @@ -501,7 +504,10 @@ int beginTransaction ( osrfMethodContext* ctx ) { } int setSavepoint ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } int spNamePos = 0; #ifdef PCRUD @@ -546,9 +552,12 @@ int setSavepoint ( osrfMethodContext* ctx ) { } int releaseSavepoint ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } - int spNamePos = 0; + int spNamePos = 0; #ifdef PCRUD spNamePos = 1; jsonObject* user = verifyUserPCRUD( ctx ); @@ -591,9 +600,12 @@ int releaseSavepoint ( osrfMethodContext* ctx ) { } int rollbackSavepoint ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } - int spNamePos = 0; + int spNamePos = 0; #ifdef PCRUD spNamePos = 1; jsonObject* user = verifyUserPCRUD( ctx ); @@ -636,7 +648,10 @@ int rollbackSavepoint ( osrfMethodContext* ctx ) { } int commitTransaction ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } #ifdef PCRUD jsonObject* user = verifyUserPCRUD( ctx ); @@ -664,7 +679,10 @@ int commitTransaction ( osrfMethodContext* ctx ) { } int rollbackTransaction ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } #ifdef PCRUD jsonObject* user = verifyUserPCRUD( ctx ); @@ -692,9 +710,12 @@ int rollbackTransaction ( osrfMethodContext* ctx ) { } int dispatchCRUDMethod ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } - osrfHash* meta = (osrfHash*) ctx->method->userData; + osrfHash* meta = (osrfHash*) ctx->method->userData; osrfHash* class_obj = osrfHashGet( meta, "class" ); int err = 0; @@ -763,7 +784,7 @@ int dispatchCRUDMethod ( osrfMethodContext* ctx ) { jsonObjectSetIndex( _p, 1, jsonNewObjectType(JSON_HASH) ); } - jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "no_i18n", jsonParseString("true") ); + jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), "no_i18n", jsonNewBoolObject( 1 ) ); jsonObjectSetKey( jsonObjectGetIndex( _p, 1 ), @@ -913,7 +934,6 @@ static int verifyObjectPCRUD ( osrfMethodContext* ctx, const jsonObject* obj ) jsonObjectFree(user); osrfStringArray* permission = osrfHashGet(pcrud, "permission"); - char* global_required = osrfHashGet(pcrud, "global_required"); osrfStringArray* local_context = osrfHashGet(pcrud, "local_context"); osrfHash* foreign_context = osrfHashGet(pcrud, "foreign_context"); @@ -921,7 +941,7 @@ static int verifyObjectPCRUD ( osrfMethodContext* ctx, const jsonObject* obj ) int err = 0; char* pkey_value = NULL; - if (global_required && !strcmp( "true", global_required )) { + if ( str_is_true( osrfHashGet(pcrud, "global_required") ) ) { osrfLogDebug( OSRF_LOG_MARK, "global-level permissions required, fetching top of the org tree" ); // check for perm at top of org tree @@ -1290,7 +1310,9 @@ static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) { return jsonNULL; } - if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) { + // The following test is harmless but redundant. If a class is + // readonly, we don't register a create method for it. + if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) { osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, @@ -1338,7 +1360,8 @@ static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) { osrfHash* field = osrfHashGet( fields, field_name ); - if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue; + if( str_is_true( osrfHashGet( field, "virtual" ) ) ) + continue; const jsonObject* field_object = oilsFMGetObject( target, field_name ); @@ -1458,7 +1481,7 @@ static jsonObject* doCreate(osrfMethodContext* ctx, int* err ) { quiet_str = jsonObjectToSimpleString( quiet_obj ); } - if( quiet_str && !strcmp( quiet_str, "true" )) { // if quietness is specified + if( str_is_true( quiet_str ) ) { // if quietness is specified obj = jsonNewObject(id); } else { @@ -1508,8 +1531,6 @@ static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) { osrfHash* meta = osrfHashGet( (osrfHash*) ctx->method->userData, "class" ); - jsonObject* obj; - char* id = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, id_pos)); jsonObject* order_hash = jsonObjectGetIndex(ctx->params, order_pos); @@ -1541,7 +1562,7 @@ static jsonObject* doRetrieve(osrfMethodContext* ctx, int* err ) { return jsonNULL; } - obj = jsonObjectClone( jsonObjectGetIndex(list, 0) ); + jsonObject* obj = jsonObjectClone( jsonObjectGetIndex(list, 0) ); jsonObjectFree( list ); jsonObjectFree( fake_params ); @@ -1591,7 +1612,7 @@ static char* jsonNumberToDBString ( osrfHash* field, const jsonObject* value ) { } static char* searchINPredicate (const char* class, osrfHash* field, - const jsonObject* node, const char* op) { + jsonObject* node, const char* op, osrfMethodContext* ctx ) { growing_buffer* sql_buf = buffer_init(32); buffer_fadd( @@ -1609,76 +1630,100 @@ static char* searchINPredicate (const char* class, osrfHash* field, buffer_add(sql_buf, "IN ("); } - int in_item_index = 0; - int in_item_first = 1; - jsonObject* in_item; - while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) { - - if (in_item_first) - in_item_first = 0; - else - buffer_add(sql_buf, ", "); - - if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) { - char* val = jsonNumberToDBString( field, in_item ); - OSRF_BUFFER_ADD( sql_buf, val ); - free(val); + if (node->type == JSON_HASH) { + // subquery predicate + char* subpred = SELECT( + ctx, + jsonObjectGetKey( node, "select" ), + jsonObjectGetKey( node, "from" ), + jsonObjectGetKey( node, "where" ), + jsonObjectGetKey( node, "having" ), + jsonObjectGetKey( node, "order_by" ), + jsonObjectGetKey( node, "limit" ), + jsonObjectGetKey( node, "offset" ), + SUBSELECT + ); - } else { - char* key_string = jsonObjectToSimpleString(in_item); - if ( dbi_conn_quote_string(dbhandle, &key_string) ) { - OSRF_BUFFER_ADD( sql_buf, key_string ); - free(key_string); - } else { - osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string); - free(key_string); - buffer_free(sql_buf); - return NULL; - } - } - } + buffer_add(sql_buf, subpred); + free(subpred); + } else if (node->type == JSON_ARRAY) { + // litteral value list + int in_item_index = 0; + int in_item_first = 1; + jsonObject* in_item; + while ( (in_item = jsonObjectGetIndex(node, in_item_index++)) ) { + + if (in_item_first) + in_item_first = 0; + else + buffer_add(sql_buf, ", "); + + if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) { + char* val = jsonNumberToDBString( field, in_item ); + OSRF_BUFFER_ADD( sql_buf, val ); + free(val); + + } else { + char* key_string = jsonObjectToSimpleString(in_item); + if ( dbi_conn_quote_string(dbhandle, &key_string) ) { + OSRF_BUFFER_ADD( sql_buf, key_string ); + free(key_string); + } else { + osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, key_string); + free(key_string); + buffer_free(sql_buf); + return NULL; + } + } + } + } + OSRF_BUFFER_ADD_CHAR( sql_buf, ')' ); return buffer_release(sql_buf); } +// Receive a JSON_ARRAY representing a function call. The first +// entry in the array is the function name. The rest are parameters. static char* searchValueTransform( const jsonObject* array ) { growing_buffer* sql_buf = buffer_init(32); char* val = NULL; - int func_item_index = 0; - int func_item_first = 2; jsonObject* func_item; + + // Get the function name + if( array->size > 0 ) { + func_item = jsonObjectGetIndex( array, 0 ); + val = jsonObjectToSimpleString( func_item ); + OSRF_BUFFER_ADD( sql_buf, val ); + OSRF_BUFFER_ADD( sql_buf, "( " ); + free(val); + } + + // Get the parameters + int func_item_index = 1; // We already grabbed the zeroth entry while ( (func_item = jsonObjectGetIndex(array, func_item_index++)) ) { - val = jsonObjectToSimpleString(func_item); - - if (func_item_first == 2) { - OSRF_BUFFER_ADD(sql_buf, val); - OSRF_BUFFER_ADD(sql_buf, "( "); - free(val); - func_item_first--; - continue; - } - - if (func_item_first) - func_item_first--; - else - buffer_add(sql_buf, ", "); + // Add a separator comma, if we need one + if( func_item_index > 2 ) + buffer_add( sql_buf, ", " ); + // Add the current parameter if (func_item->type == JSON_NULL) { buffer_add( sql_buf, "NULL" ); - } else if ( dbi_conn_quote_string(dbhandle, &val) ) { - OSRF_BUFFER_ADD( sql_buf, val ); } else { - osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val); - free(val); - buffer_free(sql_buf); - return NULL; + val = jsonObjectToSimpleString(func_item); + if ( dbi_conn_quote_string(dbhandle, &val) ) { + OSRF_BUFFER_ADD( sql_buf, val ); + free(val); + } else { + osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val); + buffer_free(sql_buf); + free(val); + return NULL; + } } - - free(val); } buffer_add( sql_buf, " )" ); @@ -1706,12 +1751,18 @@ static char* searchFunctionPredicate (const char* class, osrfHash* field, return buffer_release(sql_buf); } +// class is a class name +// field is a field definition as stored in the IDL +// node comes from the method parameter, and represents an entry in the SELECT list static char* searchFieldTransform (const char* class, osrfHash* field, const jsonObject* node) { growing_buffer* sql_buf = buffer_init(32); char* field_transform = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "transform" ) ); char* transform_subcolumn = jsonObjectToSimpleString( jsonObjectGetKeyConst( node, "result_field" ) ); + if(transform_subcolumn) + OSRF_BUFFER_ADD_CHAR( sql_buf, '(' ); // enclose transform in parentheses + if (field_transform) { buffer_fadd( sql_buf, "%s(\"%s\".%s", field_transform, class, osrfHashGet(field, "name")); const jsonObject* array = jsonObjectGetKeyConst( node, "params" ); @@ -1730,36 +1781,24 @@ static char* searchFieldTransform (const char* class, osrfHash* field, const jso OSRF_BUFFER_ADD( sql_buf, val ); } else { osrfLogError(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, val); - free(field_transform); + free(transform_subcolumn); + free(field_transform); free(val); buffer_free(sql_buf); return NULL; } free(val); } - } - buffer_add( - sql_buf, - " )" - ); + buffer_add( sql_buf, " )" ); } else { buffer_fadd( sql_buf, "\"%s\".%s", class, osrfHashGet(field, "name")); } - if (transform_subcolumn) { - char * tmp = buffer_release(sql_buf); - sql_buf = buffer_init(32); - buffer_fadd( - sql_buf, - "(%s).\"%s\"", - tmp, - transform_subcolumn - ); - free(tmp); - } + if (transform_subcolumn) + buffer_fadd( sql_buf, ").\"%s\"", transform_subcolumn ); if (field_transform) free(field_transform); if (transform_subcolumn) free(transform_subcolumn); @@ -1890,11 +1929,12 @@ static char* searchBETWEENPredicate (const char* class, osrfHash* field, jsonObj return buffer_release(sql_buf); } -static char* searchPredicate ( const char* class, osrfHash* field, jsonObject* node ) { +static char* searchPredicate ( const char* class, osrfHash* field, + jsonObject* node, osrfMethodContext* ctx ) { char* pred = NULL; if (node->type == JSON_ARRAY) { // equality IN search - pred = searchINPredicate( class, field, node, NULL ); + pred = searchINPredicate( class, field, node, NULL, ctx ); } else if (node->type == JSON_HASH) { // non-equality search jsonObject* pred_node; jsonIterator* pred_itr = jsonNewIterator( node ); @@ -1902,7 +1942,7 @@ static char* searchPredicate ( const char* class, osrfHash* field, jsonObject* n if ( !(strcasecmp( pred_itr->key,"between" )) ) pred = searchBETWEENPredicate( class, field, pred_node ); else if ( !(strcasecmp( pred_itr->key,"in" )) || !(strcasecmp( pred_itr->key,"not in" )) ) - pred = searchINPredicate( class, field, pred_node, pred_itr->key ); + pred = searchINPredicate( class, field, pred_node, pred_itr->key, ctx ); else if ( pred_node->type == JSON_ARRAY ) pred = searchFunctionPredicate( class, field, pred_node, pred_itr->key ); else if ( pred_node->type == JSON_HASH ) @@ -1971,21 +2011,28 @@ static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) { free(_tmp); working_hash = freeable_hash; } - else + else { + if( join_hash->type != JSON_HASH ) { + osrfLogError( + OSRF_LOG_MARK, + "%s: JOIN failed; expected JSON object type not found", + MODULENAME + ); + return NULL; + } working_hash = join_hash; + } growing_buffer* join_buf = buffer_init(128); - char* leftclass = osrfHashGet(leftmeta, "classname"); + const char* leftclass = osrfHashGet(leftmeta, "classname"); jsonObject* snode = NULL; jsonIterator* search_itr = jsonNewIterator( working_hash ); - if(freeable_hash) - jsonObjectFree(freeable_hash); while ( (snode = jsonIteratorNext( search_itr )) ) { osrfHash* idlClass = osrfHashGet( oilsIDL(), search_itr->key ); - char* class = osrfHashGet(idlClass, "classname"); + const char* class = osrfHashGet(idlClass, "classname"); char* fkey = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "fkey" ) ); char* field = jsonObjectToSimpleString( jsonObjectGetKeyConst( snode, "field" ) ); @@ -2002,6 +2049,8 @@ static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) { leftclass ); buffer_free(join_buf); + if(freeable_hash) + jsonObjectFree(freeable_hash); free(field); jsonIteratorFree(search_itr); return NULL; @@ -2020,6 +2069,8 @@ static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) { class ); buffer_free(join_buf); + if(freeable_hash) + jsonObjectFree(freeable_hash); free(fkey); jsonIteratorFree(search_itr); return NULL; @@ -2029,37 +2080,46 @@ static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) { } else if (!field && !fkey) { osrfHash* _links = oilsIDLFindPath("/%s/links", leftclass); - int i = 0; - osrfStringArray* keys = osrfHashKeys( _links ); - while ( (fkey = osrfStringArrayGetString(keys, i++)) ) { - fkey = strdup(osrfStringArrayGetString(keys, i++)); - if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", leftclass, fkey), class) ) { - field = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", leftclass, fkey) ); + // For each link defined for the left class: + // see if the link references the joined class + osrfHashIterator* itr = osrfNewHashIterator( _links ); + osrfHash* curr_link = NULL; + while( (curr_link = osrfHashIteratorNext( itr ) ) ) { + const char* other_class = osrfHashGet( curr_link, "class" ); + if( other_class && !strcmp( other_class, class ) ) { + + // Found a link between the classes + fkey = strdup( osrfHashIteratorKey( itr ) ); + const char* other_key = osrfHashGet( curr_link, "key" ); + field = other_key ? strdup( other_key ) : NULL; break; - } else { - free(fkey); } } - osrfStringArrayFree(keys); - - if (!field && !fkey) { + osrfHashIteratorFree( itr ); + + if (!field || !fkey) { + // Do another such search, with the classes reversed _links = oilsIDLFindPath("/%s/links", class); - i = 0; - keys = osrfHashKeys( _links ); - while ( (field = osrfStringArrayGetString(keys, i++)) ) { - field = strdup(osrfStringArrayGetString(keys, i++)); - if ( !strcmp( (char*)oilsIDLFindPath("/%s/links/%s/class", class, field), class) ) { - fkey = strdup( (char*)oilsIDLFindPath("/%s/links/%s/key", class, field) ); + // For each link defined for the joined class: + // see if the link references the left class + osrfHashIterator* itr = osrfNewHashIterator( _links ); + osrfHash* curr_link = NULL; + while( (curr_link = osrfHashIteratorNext( itr ) ) ) { + const char* other_class = osrfHashGet( curr_link, "class" ); + if( other_class && !strcmp( other_class, leftclass ) ) { + + // Found a link between the classes + fkey = strdup( osrfHashIteratorKey( itr ) ); + const char* other_key = osrfHashGet( curr_link, "key" ); + field = other_key ? strdup( other_key ) : NULL; break; - } else { - free(field); } } - osrfStringArrayFree(keys); + osrfHashIteratorFree( itr ); } - if (!field && !fkey) { + if (!field || !fkey) { osrfLogError( OSRF_LOG_MARK, "%s: JOIN failed. No link defined between %s and %s", @@ -2068,6 +2128,8 @@ static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) { class ); buffer_free(join_buf); + if(freeable_hash) + jsonObjectFree(freeable_hash); jsonIteratorFree(search_itr); return NULL; } @@ -2091,7 +2153,8 @@ static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) { free(type); char* table = getSourceDefinition(idlClass); - buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", table, class, class, field, leftclass, fkey); + buffer_fadd(join_buf, " %s AS \"%s\" ON ( \"%s\".%s = \"%s\".%s", + table, class, class, field, leftclass, fkey); free(table); const jsonObject* filter = jsonObjectGetKeyConst( snode, "filter" ); @@ -2128,7 +2191,9 @@ static char* searchJOIN ( const jsonObject* join_hash, osrfHash* leftmeta ) { free(field); } - jsonIteratorFree(search_itr); + if(freeable_hash) + jsonObjectFree(freeable_hash); + jsonIteratorFree(search_itr); return buffer_release(join_buf); } @@ -2145,7 +2210,7 @@ static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int op osrfLogDebug( OSRF_LOG_MARK, - "%s: Entering searchWHERE; search_hash addr = %d, meta addr = %d, opjoin_type = %d, ctx addr = %d", + "%s: Entering searchWHERE; search_hash addr = %p, meta addr = %p, opjoin_type = %d, ctx addr = %p", MODULENAME, search_hash, meta, @@ -2246,7 +2311,7 @@ static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int op char* table = getSourceDefinition(meta); osrfLogError( OSRF_LOG_MARK, - "%s: Attempt to reference non-existant column %s on %s (%s)", + "%s: Attempt to reference non-existent column %s on %s (%s)", MODULENAME, search_itr->key, table, @@ -2258,7 +2323,7 @@ static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int op return NULL; } - char* subpred = searchPredicate( class, field, node ); + char* subpred = searchPredicate( class, field, node, ctx ); buffer_add( sql_buf, subpred ); free(subpred); } @@ -2283,7 +2348,7 @@ static char* searchWHERE ( const jsonObject* search_hash, osrfHash* meta, int op return buffer_release(sql_buf); } -static char* SELECT ( +char* SELECT ( /* method context */ osrfMethodContext* ctx, /* SELECT */ jsonObject* selhash, @@ -2302,12 +2367,10 @@ static char* SELECT ( // general tmp objects const jsonObject* tmp_const; - jsonObject* _tmp = NULL; jsonObject* selclass = NULL; jsonObject* selfield = NULL; jsonObject* snode = NULL; jsonObject* onode = NULL; - jsonObject* found = NULL; char* string = NULL; int from_function = 0; @@ -2320,8 +2383,6 @@ static char* SELECT ( // metadata about the core search class osrfHash* core_meta = NULL; - osrfHash* core_fields = NULL; - osrfHash* idlClass = NULL; // punt if there's no core class if (!join_hash || ( join_hash->type == JSON_HASH && !join_hash->size )) @@ -2334,18 +2395,27 @@ static char* SELECT ( core_class = strdup( tmp_itr->key ); join_hash = snode; + + jsonObject* extra = jsonIteratorNext( tmp_itr ); jsonIteratorFree( tmp_itr ); snode = NULL; + + // There shouldn't be more than one entry in join_hash + if( extra ) + return NULL; // Malformed join_hash; extra entry } else if (join_hash->type == JSON_ARRAY) { - from_function = 1; - selhash = NULL; + from_function = 1; + core_class = jsonObjectToSimpleString( jsonObjectGetIndex(join_hash, 0) ); + selhash = NULL; } else if (join_hash->type == JSON_STRING) { core_class = jsonObjectToSimpleString( join_hash ); join_hash = NULL; } + else + return NULL; // punt if we don't know about the core class (and it's not a function) if (!from_function && !(core_meta = osrfHashGet( oilsIDL(), core_class ))) { @@ -2376,153 +2446,175 @@ static char* SELECT ( growing_buffer* group_buf = buffer_init(128); growing_buffer* having_buf = buffer_init(128); - if (!from_function) - core_fields = osrfHashGet(core_meta, "fields"); + // Build a select list + if(from_function) // From a function we select everything + OSRF_BUFFER_ADD_CHAR( select_buf, '*' ); + else { - // ... and if we /are/ building the default list, do that - if ( (_tmp = jsonObjectGetKey(selhash,core_class)) && !_tmp->size ) { - - int i = 0; - char* field; + // If we need to build a default list, do so + jsonObject* _tmp = jsonObjectGetKey( selhash, core_class ); + if ( _tmp && !_tmp->size ) { + + int i = 0; + char* field; + osrfHash* core_fields = osrfHashGet( core_meta, "fields" ); - if (!from_function) { osrfStringArray* keys = osrfHashKeys( core_fields ); - while ( (field = osrfStringArrayGetString(keys, i++)) ) { - if ( strncasecmp( "true", osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ), 4 ) ) - jsonObjectPush( _tmp, jsonNewObject( field ) ); + while ( (field = osrfStringArrayGetString(keys, i++)) ) { + if( ! str_is_true( osrfHashGet( osrfHashGet( core_fields, field ), "virtual" ) ) ) + jsonObjectPush( _tmp, jsonNewObject( field ) ); // not virtual; use it } - osrfStringArrayFree(keys); - } - } - - // Now we build the actual select list - if (!from_function) { + osrfStringArrayFree(keys); + } + + // Now build the actual select list int sel_pos = 1; jsonObject* is_agg = jsonObjectFindPath(selhash, "//aggregate"); first = 1; gfirst = 1; jsonIterator* selclass_itr = jsonNewIterator( selhash ); - while ( (selclass = jsonIteratorNext( selclass_itr )) ) { + while ( (selclass = jsonIteratorNext( selclass_itr )) ) { // For each class // round trip through the idl, just to be safe - idlClass = osrfHashGet( oilsIDL(), selclass_itr->key ); - if (!idlClass) continue; - char* cname = osrfHashGet(idlClass, "classname"); + const char* cname = selclass_itr->key; + osrfHash* idlClass = osrfHashGet( oilsIDL(), cname ); + if (!idlClass) + // No such class. Skip it. + continue; - // make sure the target relation is in the join tree - if (strcmp(core_class,cname)) { - if (!join_hash) continue; + // Make sure the target relation is in the join tree. + + // At this point join_hash is a step down from the join_hash we + // received as a parameter. If the original was a JSON_STRING, + // then json_hash is now NULL. If the original was a JSON_HASH, + // then json_hash is now the first (and only) entry in it, + // denoting the core class. We've already excluded the + // possibility that the original was a JSON_ARRAY, because in + // that case from_function would be non-NULL, and we wouldn't + // be here. + + if ( strcmp( core_class, cname )) { + if (!join_hash) + // There's only one class in the FROM clause, + // and this isn't it. Skip it. + continue; - if (join_hash->type == JSON_STRING) { + if (join_hash->type == JSON_STRING) { string = jsonObjectToSimpleString(join_hash); - found = strcmp(string,cname) ? NULL : jsonParseString("{\"1\":\"1\"}"); + int different = strcmp( string, cname ); free(string); - } else { - found = jsonObjectFindPath(join_hash, "//%s", cname); - } - - if (!found->size) { - jsonObjectFree(found); - continue; - } - - jsonObjectFree(found); + if ( different ) + // There's only one class in the FROM clause, + // and this isn't it. Skip it. + continue; + } else { + jsonObject* found = jsonObjectFindPath(join_hash, "//%s", cname); + unsigned long size = found->size; + jsonObjectFree( found ); + if ( 0 == size ) + // No such class anywhere in the join tree. Skip it. + continue; + } } + // Look up some attributes of the current class, so that we + // don't have to look them up again for each field + osrfHash* class_field_set = osrfHashGet( idlClass, "fields" ); + char* class_pkey = osrfHashGet( idlClass, "primarykey" ); + char* class_tname = osrfHashGet( idlClass, "tablename" ); + // stitch together the column list ... jsonIterator* select_itr = jsonNewIterator( selclass ); - while ( (selfield = jsonIteratorNext( select_itr )) ) { + while ( (selfield = jsonIteratorNext( select_itr )) ) { // for each SELECT column - char* __column = NULL; - char* __alias = NULL; + // If we need a separator comma, add one + if (first) { + first = 0; + } else { + OSRF_BUFFER_ADD_CHAR( select_buf, ',' ); + } - // ... if it's a sstring, just toss it on the pile + // ... if it's a string, just toss it on the pile if (selfield->type == JSON_STRING) { // again, just to be safe - char* _requested_col = jsonObjectToSimpleString(selfield); - osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), _requested_col ); - free(_requested_col); - - if (!field) continue; - __column = strdup(osrfHashGet(field, "name")); + const char* _requested_col = selfield->value.s; + osrfHash* field = osrfHashGet( class_field_set, _requested_col ); + if (!field) continue; // No such field in current class; skip it - if (first) { - first = 0; - } else { - OSRF_BUFFER_ADD_CHAR( select_buf, ',' ); - } + const char* col_name = osrfHashGet(field, "name"); if (locale) { - char* i18n = osrfHashGet(field, "i18n"); + const char* i18n; if (flags & DISABLE_I18N) i18n = NULL; + else + i18n = osrfHashGet(field, "i18n"); - if ( i18n && !strncasecmp("true", i18n, 4)) { - char* pkey = osrfHashGet(idlClass, "primarykey"); - char* tname = osrfHashGet(idlClass, "tablename"); - - buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, cname, __column, pkey, cname, pkey, locale, __column); + if( str_is_true( i18n ) ) { + buffer_fadd( select_buf, + " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", + class_tname, cname, col_name, class_pkey, cname, class_pkey, locale, col_name ); } else { - buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column); + buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name ); } } else { - buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, __column, __column); + buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, col_name, col_name ); } - + // ... but it could be an object, in which case we check for a Field Transform } else { - __column = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield, "column" ) ); + char* _column = jsonObjectToSimpleString( jsonObjectGetKeyConst( selfield, "column" ) ); - // again, just to be safe - osrfHash* field = osrfHashGet( osrfHashGet( idlClass, "fields" ), __column ); - if (!field) continue; - const char* fname = osrfHashGet(field, "name"); + // Get the field definition from the IDL + osrfHash* field = osrfHashGet( class_field_set, _column ); + if (!field) continue; // No such field defined in IDL. Skip it. - if (first) { - first = 0; - } else { - OSRF_BUFFER_ADD_CHAR( select_buf, ',' ); + // Decide what to use as a column alias + char* _alias; + if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) { + _alias = jsonObjectToSimpleString( tmp_const ); + free(_column); + } else { // Use column name as its own alias + _alias = _column; } + _column = NULL; // To emphasize that we're through with _column - if ((tmp_const = jsonObjectGetKeyConst( selfield, "alias" ))) { - __alias = jsonObjectToSimpleString( tmp_const ); - } else { - __alias = strdup(__column); - } + if (jsonObjectGetKeyConst( selfield, "transform" )) { + char* transform_str = searchFieldTransform(cname, field, selfield); + buffer_fadd(select_buf, " %s AS \"%s\"", transform_str, _alias); + free(transform_str); + } else { + + const char* fname = osrfHashGet(field, "name"); - if (jsonObjectGetKeyConst( selfield, "transform" )) { - free(__column); - __column = searchFieldTransform(cname, field, selfield); - buffer_fadd(select_buf, " %s AS \"%s\"", __column, __alias); - } else { if (locale) { - char* i18n = osrfHashGet(field, "i18n"); + const char* i18n; if (flags & DISABLE_I18N) i18n = NULL; - - if ( i18n && !strncasecmp("true", i18n, 4)) { - char* pkey = osrfHashGet(idlClass, "primarykey"); - char* tname = osrfHashGet(idlClass, "tablename"); + else + i18n = osrfHashGet(field, "i18n"); - buffer_fadd(select_buf, " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", tname, cname, fname, pkey, cname, pkey, locale, __alias); + if( str_is_true( i18n ) ) { + buffer_fadd( select_buf, + " oils_i18n_xlate('%s', '%s', '%s', '%s', \"%s\".%s::TEXT, '%s') AS \"%s\"", + class_tname, cname, fname, class_pkey, cname, class_pkey, locale, _alias); } else { - buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias); + buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, _alias); } } else { - buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, __alias); + buffer_fadd(select_buf, " \"%s\".%s AS \"%s\"", cname, fname, _alias); } } + + free(_alias); } if (is_agg->size || (flags & SELECT_DISTINCT)) { - if ( !( - jsonBoolIsTrue( jsonObjectGetKey( selfield, "aggregate" ) ) || - ((int)jsonObjectGetNumber(jsonObjectGetKey( selfield, "aggregate" ))) == 1 // support 1/0 for perl's sake - ) - ) { + const jsonObject* aggregate_obj = jsonObjectGetKey( selfield, "aggregate" ); + if ( ! obj_is_true( aggregate_obj ) ) { if (gfirst) { gfirst = 0; } else { @@ -2538,35 +2630,30 @@ static char* SELECT ( OSRF_BUFFER_ADD_CHAR( group_buf, ',' ); } - __column = searchFieldTransform(cname, field, selfield); + _column = searchFieldTransform(cname, field, selfield); OSRF_BUFFER_ADD_CHAR(group_buf, ' '); - OSRF_BUFFER_ADD(group_buf, __column); - __column = searchFieldTransform(cname, field, selfield); + OSRF_BUFFER_ADD(group_buf, _column); + _column = searchFieldTransform(cname, field, selfield); */ } } - if (__column) free(__column); - if (__alias) free(__alias); - sel_pos++; - } + } // end while -- iterating across SELECT columns - // jsonIteratorFree(select_itr); - } + jsonIteratorFree(select_itr); + } // end while -- iterating across classes - // jsonIteratorFree(selclass_itr); + jsonIteratorFree(selclass_itr); if (is_agg) jsonObjectFree(is_agg); - } else { - OSRF_BUFFER_ADD_CHAR( select_buf, '*' ); } char* col_list = buffer_release(select_buf); char* table = NULL; - if (!from_function) table = getSourceDefinition(core_meta); - else table = searchValueTransform(join_hash); + if (from_function) table = searchValueTransform(join_hash); + else table = getSourceDefinition(core_meta); // Put it all together buffer_fadd(sql_buf, "SELECT %s FROM %s AS \"%s\" ", col_list, table, core_class ); @@ -2581,14 +2668,18 @@ static char* SELECT ( free(join_clause); } + // Build a WHERE clause, if there is one if ( search_hash ) { buffer_add(sql_buf, " WHERE "); - // and it's on the the WHERE clause + // and it's on the WHERE clause char* pred = searchWHERE( search_hash, core_meta, AND_OP_JOIN, ctx ); - if (!pred) { - if (ctx) { + if (pred) { + buffer_add(sql_buf, pred); + free(pred); + } else { + if (ctx) { osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, @@ -2604,20 +2695,21 @@ static char* SELECT ( buffer_free(sql_buf); if (defaultselhash) jsonObjectFree(defaultselhash); return NULL; - } else { - buffer_add(sql_buf, pred); - free(pred); } } + // Build a HAVING clause, if there is one if ( having_hash ) { buffer_add(sql_buf, " HAVING "); // and it's on the the WHERE clause char* pred = searchWHERE( having_hash, core_meta, AND_OP_JOIN, ctx ); - if (!pred) { - if (ctx) { + if (pred) { + buffer_add(sql_buf, pred); + free(pred); + } else { + if (ctx) { osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, @@ -2633,12 +2725,10 @@ static char* SELECT ( buffer_free(sql_buf); if (defaultselhash) jsonObjectFree(defaultselhash); return NULL; - } else { - buffer_add(sql_buf, pred); - free(pred); } } + // Build an ORDER BY clause, if there is one first = 1; jsonIterator* class_itr = jsonNewIterator( order_hash ); while ( (snode = jsonIteratorNext( class_itr )) ) { @@ -2704,7 +2794,7 @@ static char* SELECT ( buffer_add(order_buf, direction); } - } + } // end while // jsonIteratorFree(order_itr); } else if ( snode->type == JSON_ARRAY ) { @@ -2726,7 +2816,7 @@ static char* SELECT ( buffer_add(order_buf, _f); free(_f); - } + } // end while // jsonIteratorFree(order_itr); @@ -2753,10 +2843,10 @@ static char* SELECT ( return NULL; } - } - } + } // end while + // jsonIteratorFree(class_itr); + } - // jsonIteratorFree(class_itr); string = buffer_release(group_buf); @@ -2839,7 +2929,7 @@ static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrf osrfStringArray* keys = osrfHashKeys( fields ); while ( (field = osrfStringArrayGetString(keys, i++)) ) { - if ( strcasecmp( "true", osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) ) + if( ! str_is_true( osrfHashGet( osrfHashGet( fields, field ), "virtual" ) ) ) jsonObjectPush( flist, jsonNewObject( field ) ); } osrfStringArrayFree(keys); @@ -2881,13 +2971,14 @@ static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrf } if (locale) { - char* i18n = osrfHashGet(field, "i18n"); - if ( - jsonBoolIsTrue( jsonObjectGetKey( order_hash, "no_i18n" ) ) || - ((int)jsonObjectGetNumber(jsonObjectGetKey( order_hash, "no_i18n" ))) == 1 // support 1/0 for perl's sake - ) i18n = NULL; - - if ( i18n && !strncasecmp("true", i18n, 4)) { + const char* i18n; + const jsonObject* no_i18n_obj = jsonObjectGetKey( order_hash, "no_i18n" ); + if ( obj_is_true( no_i18n_obj ) ) // Suppress internationalization? + i18n = NULL; + else + i18n = osrfHashGet(field, "i18n"); + + if( str_is_true( i18n ) ) { char* pkey = osrfHashGet(idlClass, "primarykey"); char* tname = osrfHashGet(idlClass, "tablename"); @@ -3066,7 +3157,11 @@ static char* buildSELECT ( jsonObject* search_hash, jsonObject* order_hash, osrf } int doJSONSearch ( osrfMethodContext* ctx ) { - OSRF_METHOD_VERIFY_CONTEXT(ctx); + if(osrfMethodVerifyContext( ctx )) { + osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); + return -1; + } + osrfLogDebug(OSRF_LOG_MARK, "Recieved query request"); int err = 0; @@ -3076,19 +3171,13 @@ int doJSONSearch ( osrfMethodContext* ctx ) { jsonObject* hash = jsonObjectGetIndex(ctx->params, 0); - int flags = 0; - - if (jsonBoolIsTrue(jsonObjectGetKey( hash, "distinct" ))) - flags |= SELECT_DISTINCT; + int flags = 0; - if ( ((int)jsonObjectGetNumber(jsonObjectGetKey( hash, "distinct" ))) == 1 ) // support 1/0 for perl's sake - flags |= SELECT_DISTINCT; + if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) ) + flags |= SELECT_DISTINCT; - if (jsonBoolIsTrue(jsonObjectGetKey( hash, "no_i18n" ))) - flags |= DISABLE_I18N; - - if ( ((int)jsonObjectGetNumber(jsonObjectGetKey( hash, "no_i18n" ))) == 1 ) // support 1/0 for perl's sake - flags |= DISABLE_I18N; + if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) ) + flags |= DISABLE_I18N; osrfLogDebug(OSRF_LOG_MARK, "Building SQL ..."); char* sql = SELECT( @@ -3173,38 +3262,11 @@ static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta, } osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql); - dbi_result result = dbi_conn_query(dbhandle, sql); - - jsonObject* res_list = jsonNewObjectType(JSON_ARRAY); - if(result) { - osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors"); - osrfHash* dedup = osrfNewHash(); - - if (dbi_result_first_row(result)) { - /* JSONify the result */ - osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row"); - do { - obj = oilsMakeFieldmapperFromResult( result, meta ); - char* pkey_val = oilsFMGetString( obj, pkey ); - if ( osrfHashGet( dedup, pkey_val ) ) { - jsonObjectFree(obj); - free(pkey_val); - } else { - osrfHashSet( dedup, pkey_val, pkey_val ); - jsonObjectPush(res_list, obj); - } - } while (dbi_result_next_row(result)); - } else { - osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql); - } - - osrfHashFree(dedup); - /* clean up the query */ - dbi_result_free(result); - - } else { - osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", MODULENAME, osrfHashGet(meta, "fieldmapper"), sql); + dbi_result result = dbi_conn_query(dbhandle, sql); + if( NULL == result ) { + osrfLogError(OSRF_LOG_MARK, "%s: Error retrieving %s with query [%s]", + MODULENAME, osrfHashGet(meta, "fieldmapper"), sql); osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR, @@ -3214,11 +3276,37 @@ static jsonObject* doFieldmapperSearch ( osrfMethodContext* ctx, osrfHash* meta, ); *err = -1; free(sql); - jsonObjectFree(res_list); return jsonNULL; + } else { + osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors"); } + jsonObject* res_list = jsonNewObjectType(JSON_ARRAY); + osrfHash* dedup = osrfNewHash(); + + if (dbi_result_first_row(result)) { + /* JSONify the result */ + osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row"); + do { + obj = oilsMakeFieldmapperFromResult( result, meta ); + char* pkey_val = oilsFMGetString( obj, pkey ); + if ( osrfHashGet( dedup, pkey_val ) ) { + jsonObjectFree(obj); + free(pkey_val); + } else { + osrfHashSet( dedup, pkey_val, pkey_val ); + jsonObjectPush(res_list, obj); + } + } while (dbi_result_next_row(result)); + } else { + osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", + MODULENAME, sql ); + } + + osrfHashFree(dedup); + /* clean up the query */ + dbi_result_free(result); free(sql); if (res_list->size && order_hash) { @@ -3477,7 +3565,9 @@ static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) { return jsonNULL; } - if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) { + // The following test is harmless but redundant. If a class is + // readonly, we don't register an update method for it. + if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) { osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, @@ -3526,7 +3616,8 @@ static jsonObject* doUpdate(osrfMethodContext* ctx, int* err ) { osrfHash* field = osrfHashGet( fields, field_name ); if(!( strcmp( field_name, pkey ) )) continue; - if(!( strcmp( osrfHashGet(osrfHashGet(fields,field_name), "virtual"), "true" ) )) continue; + if( str_is_true( osrfHashGet(osrfHashGet(fields,field_name), "virtual") ) ) + continue; const jsonObject* field_object = oilsFMGetObject( target, field_name ); @@ -3635,7 +3726,9 @@ static jsonObject* doDelete(osrfMethodContext* ctx, int* err ) { return jsonNULL; } - if (osrfHashGet( meta, "readonly" ) && strncasecmp("true", osrfHashGet( meta, "readonly" ), 4)) { + // The following test is harmless but redundant. If a class is + // readonly, we don't register a delete method for it. + if( str_is_true( osrfHashGet( meta, "readonly" ) ) ) { osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, @@ -3746,10 +3839,12 @@ static jsonObject* oilsMakeFieldmapperFromResult( dbi_result result, osrfHash* m /* fetch the fieldmapper index */ if( (_f = osrfHashGet(fields, (char*)columnName)) ) { - char* virt = (char*)osrfHashGet(_f, "virtual"); - char* pos = (char*)osrfHashGet(_f, "array_position"); - - if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue; + + if ( str_is_true( osrfHashGet(_f, "virtual") ) ) + continue; + + const char* pos = (char*)osrfHashGet(_f, "array_position"); + if ( !pos ) continue; fmIndex = atoi( pos ); osrfLogInternal(OSRF_LOG_MARK, "... Found column at position [%s]...", pos); @@ -3903,3 +3998,36 @@ static jsonObject* oilsMakeJSONFromResult( dbi_result result ) { return object; } +// Interpret a string as true or false +static int str_is_true( const char* str ) { + if( NULL == str || strcasecmp( str, "true" ) ) + return 0; + else + return 1; +} + +// Interpret a jsonObject as true or false +static int obj_is_true( const jsonObject* obj ) { + if( !obj ) + return 0; + else switch( obj->type ) + { + case JSON_BOOL : + if( obj->value.b ) + return 1; + else + return 0; + case JSON_STRING : + if( strcasecmp( obj->value.s, "true" ) ) + return 0; + else + return 1; + case JSON_NUMBER : // Support 1/0 for perl's sake + if( jsonObjectGetNumber( obj ) == 1.0 ) + return 1; + else + return 0; + default : + return 0; + } +} diff --git a/Open-ILS/src/c-apps/oils_idl-core.c b/Open-ILS/src/c-apps/oils_idl-core.c index f1b8b42ad3..6d511e5bab 100644 --- a/Open-ILS/src/c-apps/oils_idl-core.c +++ b/Open-ILS/src/c-apps/oils_idl-core.c @@ -66,6 +66,15 @@ osrfHash* oilsIDLInit( const char* idl_filename ) { ); } + if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "restrict_primary", BAD_CAST PERSIST_NS))) { + osrfLogDebug(OSRF_LOG_MARK, "Delete restriction policy set at '%s' for pkey of class %s", prop_str, current_class_name ); + osrfHashSet( + class_def_hash, + strdup( prop_str ), + "restrict_primary" + ); + } + if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "virtual", BAD_CAST PERSIST_NS))) { osrfHashSet( class_def_hash, diff --git a/Open-ILS/src/c-apps/test_json_query.c b/Open-ILS/src/c-apps/test_json_query.c new file mode 100644 index 0000000000..dc37e4c5dd --- /dev/null +++ b/Open-ILS/src/c-apps/test_json_query.c @@ -0,0 +1,261 @@ +/* +Copyright (C) 2009 Georgia Public Library Service +Scott McKellar + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Description : Translates a JSON query into SQL and writes the + results to standard output. Synopsis: + + test_json_query [-i IDL_file] [-f file_name] [-v] query + + -i supplies the name of the IDL file. If no IDL file is specified, + json_test_query uses the value of the environmental variable + OILS_IDL_FILENAME, if it is defined, or defaults to + "/openils/conf/fm_IDL.xml". + + -f supplies the name of a text file containing the JSON query to + be translated. A file name constisting of a single hyphen + denotes standard input. If this option is present, all + non-option arguments are ignored. + + -v verbose; outputs the name of the IDL file and the text of the + JSON query. + + If there is no -f option supplied, json_query translates the + first non-option parameter. This parameter is subject to the + usual mangling by the shell. In most cases it will be sufficient + to enclose it in single quotes, but of course any single quotes + embedded within the query will need to be escaped. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DISABLE_I18N 2 +#define SELECT_DISTINCT 1 + +// Prototype for SELECT(), which is not in any header +char* SELECT ( + /* method context */ osrfMethodContext* ctx, + + /* SELECT */ jsonObject* selhash, + /* FROM */ jsonObject* join_hash, + /* WHERE */ jsonObject* search_hash, + /* HAVING */ jsonObject* having_hash, + /* ORDER BY */ jsonObject* order_hash, + /* LIMIT */ jsonObject* limit, + /* OFFSET */ jsonObject* offset, + /* flags */ int flags +); + +static int obj_is_true( const jsonObject* obj ); +static int test_json_query( const char* json_query ); +static char* load_query( const char* filename ); + +int main( int argc, char* argv[] ) { + + // Parse command line + + const char* idl_file_name = NULL; + const char* query_file_name = NULL; + int verbose = 0; // boolean + + int opt; + opterr = 0; + const char optstring[] = ":f:i:v"; + + while( ( opt = getopt( argc, argv, optstring ) ) != -1 ) { + switch( opt ) + { + case 'f' : // get file name of query + if( query_file_name ) { + fprintf( stderr, "Multiple input files not allowed\n" ); + return EXIT_FAILURE; + } + else + query_file_name = optarg; + break; + case 'i' : // get name of IDL file + if( idl_file_name ) { + fprintf( stderr, "Multiple IDL file names not allowed\n" ); + return EXIT_FAILURE; + } + else + idl_file_name = optarg; + break; + case 'v' : // Verbose + verbose = 1; + break; + case '?' : // Invalid option + fprintf( stderr, "Invalid option '-%c' on command line\n", + (char) optopt ); + return EXIT_FAILURE; + default : // Huh? + fprintf( stderr, "Internal error: unexpected value '%c'" + "for optopt", (char) optopt ); + return EXIT_FAILURE; + + } + } + + // If the command line doesn't specify an IDL file, get it + // from an environmental variable, or apply a default + if( NULL == idl_file_name ) { + idl_file_name = getenv( "OILS_IDL_FILENAME" ); + if( NULL == idl_file_name ) + idl_file_name = "/openils/conf/fm_IDL.xml"; + } + + if( verbose ) + printf( "IDL file: %s\n", idl_file_name ); + + char* loaded_json = NULL; + const char* json_query = NULL; + + // Get the JSON query into a string + if( query_file_name ) { // Got a file? Load it + if( optind < argc ) + fprintf( stderr, "Extra parameter(s) ignored\n" ); + loaded_json = load_query( query_file_name ); + if( !loaded_json ) + return EXIT_FAILURE; + json_query = loaded_json; + } else { // No file? Use command line parameter + if ( optind == argc ) { + fprintf( stderr, "No JSON query specified\n" ); + return EXIT_FAILURE; + } else + json_query = argv[ optind ]; + } + + if( verbose ) + printf( "JSON query: %s\n", json_query ); + + osrfLogSetLevel( OSRF_LOG_WARNING ); // Suppress informational messages + (void) oilsIDLInit( idl_file_name ); // Load IDL into memory + + // Translate the JSON into SQL + int rc = test_json_query( json_query ); + + if( loaded_json ) + free( loaded_json ); + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} + +static int test_json_query( const char* json_query ) { + + jsonObject* hash = jsonParseString( json_query ); + if( !hash ) { + fprintf( stderr, "Invalid JSON\n" ); + return -1; + } + + int flags = 0; + + if ( obj_is_true( jsonObjectGetKey( hash, "distinct" ) ) ) + flags |= SELECT_DISTINCT; + + if ( obj_is_true( jsonObjectGetKey( hash, "no_i18n" ) ) ) + flags |= DISABLE_I18N; + + char* sql_query = SELECT( + NULL, + jsonObjectGetKey( hash, "select" ), + jsonObjectGetKey( hash, "from" ), + jsonObjectGetKey( hash, "where" ), + jsonObjectGetKey( hash, "having" ), + jsonObjectGetKey( hash, "order_by" ), + jsonObjectGetKey( hash, "limit" ), + jsonObjectGetKey( hash, "offset" ), + flags + ); + + if ( !sql_query ) { + fprintf( stderr, "Invalid query\n" ); + return -1; + } + else + printf( "%s\n", sql_query ); + + free( sql_query ); + jsonObjectFree( hash ); + return 0; +} + +// Interpret a jsonObject as true or false +static int obj_is_true( const jsonObject* obj ) { + if( !obj ) + return 0; + else switch( obj->type ) + { + case JSON_BOOL : + if( obj->value.b ) + return 1; + else + return 0; + case JSON_STRING : + if( strcasecmp( obj->value.s, "true" ) ) + return 0; + else + return 1; + case JSON_NUMBER : // Support 1/0 for perl's sake + if( jsonObjectGetNumber( obj ) == 1.0 ) + return 1; + else + return 0; + default : + return 0; + } +} + +static char* load_query( const char* filename ) { + FILE* fp; + + // Sanity check + if( ! filename || ! *filename ) { + fprintf( stderr, "Name of query file is empty or missing\n" ); + return NULL; + } + + // Open query file, or use standard input + if( ! strcmp( filename, "-" ) ) + fp = stdin; + else { + fp = fopen( filename, "r" ); + if( !fp ) { + fprintf( stderr, "Unable to open query file \"%s\"\n", filename ); + return NULL; + } + } + + // Load file into a growing_buffer + size_t num_read; + char buf[ BUFSIZ + 1 ]; + growing_buffer* gb = buffer_init( sizeof( buf ) ); + + while( ( num_read = fread( buf, 1, sizeof( buf ) - 1, fp ) ) ) { + buf[ num_read ] = '\0'; + buffer_add( gb, buf ); + } + + if( fp != stdin ) + fclose( fp ); + + return buffer_release( gb ); +} diff --git a/Open-ILS/src/extras/fast-extract b/Open-ILS/src/extras/fast-extract index 79bf8fb078..3fcc29770e 100755 --- a/Open-ILS/src/extras/fast-extract +++ b/Open-ILS/src/extras/fast-extract @@ -58,6 +58,7 @@ $SQL = <<'SQL'; SELECT id, tcn_source, tcn_value, + deleted, REGEXP_REPLACE(marc, E'\\n','','g') AS marc FROM biblio.record_entry WHERE id = ? diff --git a/Open-ILS/src/extras/ils_events.xml b/Open-ILS/src/extras/ils_events.xml index 77deb2d573..1d31af54d5 100644 --- a/Open-ILS/src/extras/ils_events.xml +++ b/Open-ILS/src/extras/ils_events.xml @@ -719,6 +719,9 @@ An authority record queue with the same name already exists + + Responses to this survey exist + diff --git a/Open-ILS/src/extras/org_tree_html_options.pl b/Open-ILS/src/extras/org_tree_html_options.pl index 475ead0346..36c78c6598 100644 --- a/Open-ILS/src/extras/org_tree_html_options.pl +++ b/Open-ILS/src/extras/org_tree_html_options.pl @@ -6,6 +6,7 @@ use OpenSRF::System; use OpenILS::Utils::Fieldmapper; use OpenSRF::Utils::SettingsClient; use Unicode::Normalize; +use Data::Dumper; die "usage: perl org_tree_html_options.pl " unless $ARGV[1]; OpenSRF::System->bootstrap_client(config_file => $ARGV[0]); @@ -17,6 +18,12 @@ Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("ID my $ses = OpenSRF::AppSession->create("open-ils.actor"); my $tree = $ses->request("open-ils.actor.org_tree.retrieve")->gather(1); +my @types; +my $aout = $ses->request("open-ils.actor.org_types.retrieve")->gather(1); +foreach my $type (@$aout) { + $types[int($type->id)] = $type; +} + print_option($tree); $ses->disconnect(); @@ -28,7 +35,7 @@ sub print_option { my $node = shift; return unless ($node->opac_visible =~ /^[y1t]+/i); - my $depth = $node->ou_type - 1; + my $depth = $types[$node->ou_type]->depth; my $sname = entityize($node->shortname); my $name = entityize($node->name); my $kids = $node->children; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm index 010f9c7a94..39c3fdac64 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm @@ -2065,8 +2065,7 @@ sub user_transaction_history { $e->rollback; - #my @mbts = _make_mbts( @xacts ); - my @mbts = $U->make_mbts( @xacts ); + my @mbts = $U->make_mbts( $e, @xacts ); if(defined($type)) { @mbts = grep { $_->xact_type eq $type } @mbts; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm b/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm index 74ef5122ad..91676172a4 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm @@ -17,10 +17,15 @@ sub initialize { return 1; } my $svc = 'open-ils.cstore'; my $meth = 'open-ils.cstore.direct.container'; my %types; +my %ctypes; $types{'biblio'} = "$meth.biblio_record_entry_bucket"; $types{'callnumber'} = "$meth.call_number_bucket"; $types{'copy'} = "$meth.copy_bucket"; $types{'user'} = "$meth.user_bucket"; +$ctypes{'biblio'} = "container_biblio_record_entry_bucket"; +$ctypes{'callnumber'} = "container_call_number_bucket"; +$ctypes{'copy'} = "container_copy_bucket"; +$ctypes{'user'} = "container_user_bucket"; my $event; sub _sort_buckets { @@ -40,21 +45,19 @@ __PACKAGE__->register_method( NOTES sub bucket_retrieve_all { - my($self, $client, $authtoken, $userid) = @_; - - my( $staff, $evt ) = $apputils->checkses($authtoken); - return $evt if $evt; - - my( $user, $e ) = $apputils->checkrequestor( $staff, $userid, 'VIEW_CONTAINER'); - return $e if $e; - - $logger->debug("User " . $staff->id . - " retrieving all buckets for user $userid"); - + my($self, $client, $auth, $user_id) = @_; + my $e = new_editor(authtoken => $auth); + return $e->event unless $e->checkauth; + + if($e->requestor->id ne $user_id) { + return $e->event unless $e->allowed('VIEW_CONTAINER'); + } + my %buckets; - - $buckets{$_} = $apputils->simplereq( - $svc, $types{$_} . ".search.atomic", { owner => $userid } ) for keys %types; + for my $type (keys %ctypes) { + my $meth = "search_" . $ctypes{$type}; + $buckets{$type} = $e->$meth({owner => $user_id}); + } return \%buckets; } @@ -63,67 +66,88 @@ __PACKAGE__->register_method( method => "bucket_flesh", api_name => "open-ils.actor.container.flesh", argc => 3, - notes => <<" NOTES"); - Fleshes a bucket by id - PARAMS(authtoken, bucketClass, bucketId) - bucketclasss include biblio, callnumber, copy, and user. - bucketclass defaults to biblio. - If requestor ID is different than bucketOwnerId, requestor must have - VIEW_CONTAINER permissions. - NOTES - -sub bucket_flesh { - - my($self, $client, $authtoken, $class, $bucket) = @_; +); - my( $staff, $evt ) = $apputils->checkses($authtoken); - return $evt if $evt; +__PACKAGE__->register_method( + method => "bucket_flesh_pub", + api_name => "open-ils.actor.container.public.flesh", + argc => 3, +); - $logger->debug("User " . $staff->id . " retrieving bucket $bucket"); +sub bucket_flesh { + my($self, $conn, $auth, $class, $bucket_id) = @_; + my $e = new_editor(authtoken => $auth); + return $e->event unless $e->checkauth; + return _bucket_flesh($self, $conn, $e, $class, $bucket_id); +} - my $meth = $types{$class}; +sub bucket_flesh_pub { + my($self, $conn, $class, $bucket_id) = @_; + my $e = new_editor(); + return _bucket_flesh($self, $conn, $e, $class, $bucket_id); +} - my $bkt = $apputils->simplereq( $svc, "$meth.retrieve", $bucket ); - #if(!$bkt) {return undef}; - return OpenILS::Event->new('CONTAINER_NOT_FOUND', payload=>$bucket) unless $bkt; +sub _bucket_flesh { + my($self, $conn, $e, $class, $bucket_id) = @_; + my $meth = 'retrieve_' . $ctypes{$class}; + my $bkt = $e->$meth($bucket_id) or return $e->event; - if(!$bkt->pub) { - my( $user, $e ) = $apputils->checkrequestor( $staff, $bkt->owner, 'VIEW_CONTAINER' ); - return $e if $e; + unless($U->is_true($bkt->pub)) { + return undef if $self->api_name =~ /public/; + unless($bkt->owner eq $e->requestor->id) { + return $e->event unless $e->allowed('VIEW_CONTAINER', $bkt); + } } - $bkt->items( $apputils->simplereq( $svc, - "$meth"."_item.search.atomic", { bucket => $bucket } ) ); + my $fmclass = $bkt->class_name . "i"; + $meth = 'search_' . $ctypes{$class} . '_item'; + $bkt->items( + $e->$meth( + {bucket => $bucket_id}, + { order_by => {$fmclass => "pos"}, + flesh => 1, + flesh_fields => {cbrebi => ['notes']} + } + ) + ); return $bkt; } __PACKAGE__->register_method( - method => "bucket_flesh_public", - api_name => "open-ils.actor.container.public.flesh", - argc => 3, - notes => <<" NOTES"); - Fleshes a bucket by id - PARAMS(authtoken, bucketClass, bucketId) - bucketclasss include biblio, callnumber, copy, and user. - bucketclass defaults to biblio. - If requestor ID is different than bucketOwnerId, requestor must have - VIEW_CONTAINER permissions. - NOTES - -sub bucket_flesh_public { - - my($self, $client, $class, $bucket) = @_; - - my $meth = $types{$class}; - my $bkt = $apputils->simplereq( $svc, "$meth.retrieve", $bucket ); - return undef unless ($bkt and $bkt->pub); + method => "item_note_cud", + api_name => "open-ils.actor.container.item_note.cud", +); - $bkt->items( $apputils->simplereq( $svc, - "$meth"."_item.search.atomic", { bucket => $bucket } ) ); - return $bkt; +sub item_note_cud { + my($self, $conn, $auth, $class, $note) = @_; + my $e = new_editor(authtoken => $auth, xact => 1); + return $e->die_event unless $e->checkauth; + + my $meth = 'retrieve_' . $ctypes{$class}; + my $nclass = $note->class_name; + (my $iclass = $nclass) =~ s/n$//og; + + my $db_note = $e->$meth($note->id, { + flesh => 2, + flesh_fields => { + $nclass => ['item'], + $iclass => ['bucket'] + } + }); + + if($db_note->item->bucket->owner ne $e->requestor->id) { + return $e->die_event unless + $e->allowed('UPDATE_CONTAINER', $db_note->item->bucket); + } + + $meth = 'create_' . $ctypes{$class} if $note->isnew; + $meth = 'update_' . $ctypes{$class} if $note->ischanged; + $meth = 'delete_' . $ctypes{$class} if $note->isdeleted; + return $e->die_event unless $e->$meth($note); + $e->commit; } @@ -318,21 +342,37 @@ sub __item_delete { my $stat; if( $class eq 'copy' ) { + for my $note (@{$e->search_container_copy_bucket_item_note({item => $item->id})}) { + return $e->event unless + $e->delete_container_copy_bucket_item_note($note); + } return $e->event unless $stat = $e->delete_container_copy_bucket_item($item); } if( $class eq 'callnumber' ) { + for my $note (@{$e->search_container_call_number_bucket_item_note({item => $item->id})}) { + return $e->event unless + $e->delete_container_call_number_bucket_item_note($note); + } return $e->event unless $stat = $e->delete_container_call_number_bucket_item($item); } if( $class eq 'biblio' ) { + for my $note (@{$e->search_container_biblio_record_entry_bucket_item_note({item => $item->id})}) { + return $e->event unless + $e->delete_container_biblio_record_entry_bucket_item_note($note); + } return $e->event unless $stat = $e->delete_container_biblio_record_entry_bucket_item($item); } if( $class eq 'user') { + for my $note (@{$e->search_container_user_bucket_item_note({item => $item->id})}) { + return $e->event unless + $e->delete_container_user_bucket_item_note($note); + } return $e->event unless $stat = $e->delete_container_user_bucket_item($item); } diff --git a/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm index f50001584b..18db01d615 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm @@ -856,6 +856,14 @@ sub storagereq { 'open-ils.storage', $method, @params ); } +sub storagereq_xact { + my($self, $method, @params) = @_; + my $ses = $self->start_db_session(); + my $val = $ses->request($method, @params)->gather(1); + $self->rollback_db_session($ses); + return $val; +} + sub cstorereq { my( $self, $method, @params ) = @_; return $self->simplereq( @@ -1149,20 +1157,13 @@ sub patron_total_items_out { sub fetch_mbts { my $self = shift; my $id = shift; - my $editor = shift || OpenILS::Utils::CStoreEditor->new; - - $id = $id->id if (ref($id)); - - my $xact = $editor->retrieve_money_billable_transaction( - [ - $id, { - flesh => 1, - flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] } - } - ] - ) or return (undef, $editor->event); + my $e = shift || OpenILS::Utils::CStoreEditor->new; + $id = $id->id if ref($id); + + my $xact = $e->retrieve_money_billable_transaction_summary($id) + or return (undef, $e->event); - return $self->make_mbts($xact); + return ($xact); } @@ -1172,59 +1173,9 @@ sub fetch_mbts { #-------------------------------------------------------------------- sub make_mbts { my $self = shift; + my $e = shift; my @xacts = @_; - - my @mbts; - for my $x (@xacts) { - - my $s = new Fieldmapper::money::billable_transaction_summary; - - $s->id( $x->id ); - $s->usr( $x->usr ); - $s->xact_start( $x->xact_start ); - $s->xact_finish( $x->xact_finish ); - - my $to = 0; - my $lb = undef; - for my $b (@{ $x->billings }) { - next if ($self->is_true($b->voided)); - $to += ($b->amount * 100); - $lb ||= $b->billing_ts; - if ($b->billing_ts ge $lb) { - $lb = $b->billing_ts; - $s->last_billing_note($b->note); - $s->last_billing_ts($b->billing_ts); - $s->last_billing_type($b->billing_type); - } - } - - $s->total_owed( sprintf('%0.2f', $to / 100 ) ); - - my $tp = 0; - my $lp = undef; - for my $p (@{ $x->payments }) { - next if ($self->is_true($p->voided)); - $tp += ($p->amount * 100); - $lp ||= $p->payment_ts; - if ($p->payment_ts ge $lp) { - $lp = $p->payment_ts; - $s->last_payment_note($p->note); - $s->last_payment_ts($p->payment_ts); - $s->last_payment_type($p->payment_type); - } - } - - $s->total_paid( sprintf('%0.2f', $tp / 100 ) ); - $s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) ); - $s->xact_type('grocery') if ($x->grocery); - $s->xact_type('circulation') if ($x->circulation); - - $logger->debug("Created mbts with balance_owed = ". $s->balance_owed); - - push @mbts, $s; - } - - return @mbts; + return @{$e->search_money_billable_transaction_summary({id => [ map { $_->id } @xacts ]})}; } diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm index 0fccc00107..f0fb7999f5 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm @@ -197,6 +197,7 @@ sub run_method { # overrides have been performed. Go ahead and re-override. $circulator->override(1) if $circulator->request_precat; $circulator->do_permit(); + $circulator->is_checkout(1); unless( $circulator->bail_out ) { $circulator->events([]); $circulator->do_checkout(); @@ -208,6 +209,7 @@ sub run_method { return $data; } elsif( $api =~ /checkout/ ) { + $circulator->is_checkout(1); $circulator->do_checkout(); } elsif( $api =~ /checkin/ ) { @@ -244,6 +246,7 @@ sub run_method { $circulator->do_hold_notify($circulator->notify_hold) if $circulator->notify_hold; $circulator->retarget_holds if $circulator->retarget; + $circulator->append_reading_list; } } @@ -339,6 +342,7 @@ my @AUTOLOAD_FIELDS = qw/ volume title is_renewal + is_checkout is_noncat is_precat request_precat @@ -1133,7 +1137,6 @@ sub do_checkout { } if( $self->is_precat ) { - #$self->script_runner->insert("environment.isPrecat", 1, 1) $self->make_precat_copy; return if $self->bail_out; @@ -2257,7 +2260,6 @@ sub log_me { sub do_renew { my $self = shift; $self->log_me("do_renew()"); - $self->is_renewal(1); # Make sure there is an open circ to renew that is not # marked as LOST, CLAIMSRETURNED, or LONGOVERDUE @@ -2359,5 +2361,67 @@ sub run_renew_permit { } +sub append_reading_list { + my $self = shift; + + return undef unless + $self->is_checkout and + $self->patron and + $self->copy and + !$self->is_noncat; + + my $e = new_editor(xact => 1, requestor => $self->editor->requestor); + + # verify history is globally enabled and uses the bucket mechanism + my $htype = OpenSRF::Utils::SettingsClient->new->config_value( + apps => 'open-ils.circ' => app_settings => 'checkout_history_mechanism'); + + unless($htype eq 'bucket') { + $e->rollback; + return undef; + } + + # verify the patron wants to retain the hisory + my $setting = $e->search_actor_user_setting( + {usr => $self->patron->id, name => 'circ.keep_checkout_history'})->[0]; + + unless($setting and $setting->value) { + $e->rollback; + return undef; + } + + my $bkt = $e->search_container_copy_bucket( + {owner => $self->patron->id, btype => 'circ_history'})->[0]; + + my $pos = 1; + + if($bkt) { + # find the next item position + my $last_item = $e->search_container_copy_bucket_item( + {bucket => $bkt->id}, {order_by => {ccbi => 'pos desc'}, limit => 1})->[0]; + $pos = $last_item->pos + 1 if $last_item; + + } else { + # create the history bucket if necessary + $bkt = Fieldmapper::container::copy_bucket->new; + $bkt->owner($self->patron->id); + $bkt->name(''); + $bkt->btype('reading_list'); + $bkt->pub('f'); + $e->create_container_copy_bucket($bkt) or return $e->die_event; + } + + my $item = Fieldmapper::container::copy_bucket_item->new; + + $item->bucket($bkt->id); + $item->target_copy($self->copy->id); + $item->pos($pos); + + $e->create_container_copy_bucket_item($item) or return $e->die_event; + $e->commit; + + return undef; +} + diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm index d12f82e824..b777ec459d 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Holds.pm @@ -323,11 +323,12 @@ different from the user, then the requestor must have VIEW_HOLD permissions. NOTE sub retrieve_holds { - my($self, $client, $auth, $user_id) = @_; + my($self, $client, $auth, $user_id, $options) = @_; my $e = new_editor(authtoken=>$auth); return $e->event unless $e->checkauth; $user_id = $e->requestor->id unless defined $user_id; + $options ||= {}; unless($user_id == $e->requestor->id) { my $user = $e->retrieve_actor_user($user_id) or return $e->event; @@ -345,6 +346,21 @@ sub retrieve_holds { }, {order_by => {ahr => "request_time"}} ]); + + if($$options{canceled}) { + my $count = $$options{cancel_count} || + $U->ou_ancestor_setting_value($e->requestor->ws_ou, + 'circ.canceled_hold_display_count', $e) || 5; + + my $canceled = $e->search_action_hold_request([ + { usr => $user_id , + fulfillment_time => undef, + cancel_time => {'!=' => undef}, + }, + {order_by => {ahr => "cancel_time desc"}, limit => $count} + ]); + push(@$holds, @$canceled); + } if( ! $self->api_name =~ /id_list/ ) { for my $hold ( @$holds ) { @@ -438,6 +454,46 @@ sub retrieve_holds_by_pickup_lib { } } + +__PACKAGE__->register_method( + method => "uncancel_hold", + api_name => "open-ils.circ.hold.uncancel" +); + +sub uncancel_hold { + my($self, $client, $auth, $hold_id) = @_; + my $e = new_editor(authtoken=>$auth, xact=>1); + return $e->event unless $e->checkauth; + + my $hold = $e->retrieve_action_hold_request($hold_id) + or return $e->die_event; + return $e->die_event unless $e->allowed('CANCEL_HOLDS', $hold->request_lib); + + return 0 if $hold->fulfillment_time; + return 1 unless $hold->cancel_time; + + # if configured to reset the request time, also reset the expire time + if($U->ou_ancestor_setting_value( + $hold->request_lib, 'circ.hold_reset_request_time_on_uncancel', $e)) { + + $hold->request_time('now'); + my $interval = $U->ou_ancestor_setting_value($hold->request_lib, OILS_SETTING_HOLD_EXPIRE); + if($interval) { + my $date = DateTime->now->add(seconds => OpenSRF::Utils::interval_to_seconds($interval)); + $hold->expire_time($U->epoch2ISO8601($date->epoch)); + } + } + + $hold->clear_cancel_time; + $e->update_action_hold_request($hold) or return $e->die_event; + $e->commit; + + $U->storagereq('open-ils.storage.action.hold_request.copy_targeter', undef, $hold_id); + + return 1; +} + + __PACKAGE__->register_method( method => "cancel_hold", api_name => "open-ils.circ.hold.cancel", @@ -449,7 +505,7 @@ __PACKAGE__->register_method( NOTE sub cancel_hold { - my($self, $client, $auth, $holdid) = @_; + my($self, $client, $auth, $holdid, $cause, $note) = @_; my $e = new_editor(authtoken=>$auth, xact=>1); return $e->event unless $e->checkauth; @@ -496,6 +552,8 @@ sub cancel_hold { } $hold->cancel_time('now'); + $hold->cancel_cause($cause); + $hold->cancel_note($note); $e->update_action_hold_request($hold) or return $e->event; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm index 643d5e6d1a..4631b8bf97 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Survey.pm @@ -19,6 +19,7 @@ use strict; use warnings; use OpenSRF::EX qw/:try/; use OpenILS::Application::AppUtils; use Data::Dumper; +use OpenILS::Event; use Time::HiRes qw(time); use OpenILS::Utils::CStoreEditor qw/:funcs/; @@ -395,17 +396,52 @@ sub get_random_survey_global { } +__PACKAGE__->register_method ( + method => 'delete_survey', + api_name => 'open-ils.circ.survey.delete.cascade' +); +__PACKAGE__->register_method ( + method => 'delete_survey', + api_name => 'open-ils.circ.survey.delete.cascade.override' +); +sub delete_survey { + my($self, $conn, $auth, $survey_id) = @_; + my $e = new_editor(authtoken => $auth, xact => 1); + return $e->die_event unless $e->checkauth; + my $survey = $e->retrieve_action_survey($survey_id) + or return $e->die_event; + return $e->die_event unless $e->allowed('ADMIN_SURVEY', $survey->owner); + my $questions = $e->search_action_survey_question({survey => $survey_id}); + my @answers; + push(@answers, @{$e->search_action_survey_answer({question => $_->id})}) for @$questions; + my $responses = $e->search_action_survey_response({survey => $survey_id}); + return OpenILS::Event->new('SURVEY_RESPONSES_EXIST') + if @$responses and $self->api_name =! /override/; + for my $resp (@$responses) { + $e->delete_action_survey_response($resp) or return $e->die_event; + } + for my $ans (@answers) { + $e->delete_action_survey_answer($ans) or return $e->die_event; + } + for my $quest (@$questions) { + $e->delete_action_survey_question($quest) or return $e->die_event; + } -1; + $e->delete_action_survey($survey) or return $e->die_event; + + $e->commit; + return 1; +} +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm index 208d3a5e69..c8335b4217 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/action.pm @@ -93,9 +93,9 @@ use base qw/action/; __PACKAGE__->table('action_hold_request'); __PACKAGE__->columns(Primary => 'id'); __PACKAGE__->columns(Essential => qw/request_time capture_time fulfillment_time - prev_check_time expire_time requestor usr + prev_check_time expire_time requestor usr cancel_cause hold_type holdable_formats target cancel_time - phone_notify email_notify selection_depth + phone_notify email_notify selection_depth cancel_note pickup_lib current_copy request_lib frozen thaw_date fulfillment_staff fulfillment_lib selection_ou/); diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm index 226e73a9d4..d7623d0d18 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/storage.pm @@ -33,10 +33,10 @@ if (my $old_xact = $pg->current_xact_session) { if ($pg->current_xact_is_auto) { $log->debug("Commiting old autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO); - $self->pg_commit_xaction($client); + $self->method_lookup("open-ils.storage.transaction.commit")->run(); } else { $log->debug("Rolling back old NON-autocommit transaction with Open-ILS XACT-ID [$old_xact]", INFO); - $self->pg_rollback_xaction($client); + $self->method_lookup("open-ils.storage.transaction.rollback")->run(); throw OpenSRF::DomainObject::oilsException->new( statusCode => 500, status => "Previous transaction rolled back!", diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm index 5e90be8aea..8a9495797d 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm @@ -890,7 +890,7 @@ sub new_hold_copy_targeter { if ($hold->expire_time) { my $ex_time = $parser->parse_datetime( clense_ISO8601( $hold->expire_time ) ); - $hold->update( { cancel_time => 'now' } ) if ( DateTime->compare($ex_time, DateTime->now) < 0 ); + $hold->update( { cancel_cause => 1, cancel_time => 'now' } ) if ( DateTime->compare($ex_time, DateTime->now) < 0 ); $self->method_lookup('open-ils.storage.transaction.commit')->run; } diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm index 4aa853973c..1d28e18e9c 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/actor.pm @@ -484,10 +484,10 @@ sub patron_search { # group 2 = phone, ident # group 3 = barcode - my $usr = join ' AND ', map { "LOWER($_) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search; + my $usr = join ' AND ', map { "LOWER(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '0' } keys %$search; my @usrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '0' } keys %$search; - my $addr = join ' AND ', map { "LOWER($_) ~ ?" } grep { ''.$$search{$_}{group} eq '1' } keys %$search; + my $addr = join ' AND ', map { "LOWER(CAST($_ AS text)) ~ ?" } grep { ''.$$search{$_}{group} eq '1' } keys %$search; my @addrv = map { "^$$search{$_}{value}" } grep { ''.$$search{$_}{group} eq '1' } keys %$search; my $pv = $$search{phone}{value}; @@ -563,8 +563,8 @@ sub patron_search { return undef if (!$select && !$card); - my $order_by = join ', ', map { 'LOWER(users.'. (split / /,$_)[0] . ') ' . (split / /,$_)[1] } @$sort; - my $distinct_list = join ', ', map { 'LOWER(users.'. (split / /,$_)[0] . ')' } @$sort; + my $order_by = join ', ', map { 'LOWER(CAST(users.'. (split / /,$_)[0] . ' AS text)) ' . (split / /,$_)[1] } @$sort; + my $distinct_list = join ', ', map { 'LOWER(CAST(users.'. (split / /,$_)[0] . ' AS text))' } @$sort; if ($inactive) { $inactive = ''; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm b/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm index b2f47d9d07..dc1814713b 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Trigger.pm @@ -7,10 +7,15 @@ use OpenSRF::EX qw/:try/; use OpenSRF::AppSession; use OpenSRF::Utils::SettingsClient; use OpenSRF::Utils::Logger qw/:level/; +use OpenSRF::Utils qw/:datetime/; + +use DateTime; +use DateTime::Format::ISO8601; use OpenILS::Utils::Fieldmapper; use OpenILS::Utils::CStoreEditor q/:funcs/; use OpenILS::Application::Trigger::Event; +use OpenILS::Application::Trigger::EventGroup; my $log = 'OpenSRF::Utils::Logger'; @@ -18,4 +23,219 @@ my $log = 'OpenSRF::Utils::Logger'; sub initialize {} sub child_init {} +sub create_events_for_object { + my $self = shift; + my $client = shift; + my $key = shift; + my $target = shift; + my $location = shift; + + my $ident = $target->Identity; + my $ident_value = $target->$ident(); + + my $editor = new_editor(xact=>1); + + my $hooks = $editor->search_action_trigger_hook( + { key => $key, + core_type => $target->json_hint + } + ); + + my %hook_hash = map { ($_->id, $_) } @$hooks; + + my $orgs = $editor->json_query({ from => [ 'actor.org_unit_ancestors' => $location ] }); + my $defs = $editor->search_action_trigger_event_definition([ + { hook => [ keys %hook_hash ], + owner => [ map { $_->{id} } @$orgs ], + active => 't' + }, + { idlist => 1 } + ]); + + for my $def ( @$defs ) { + + my $date = DateTime->now; + + if ($hook_hash{$def->hook}->passive eq 'f') { + + if (my $dfield = $def->delay_field) { + if ($target->$dfield()) { + $date = DateTime::Format::ISO8601->new->parse_datetime( clense_ISO8601($target->$dfield) ); + } else { + next; + } + } + + $date->add( seconds => interval_to_seconds($def->delay) ); + } + + my $event = Fieldmapper::action_trigger::event->new(); + $event->target( $ident_value ); + $event->event_def( $def->id ); + $event->run_time( $date->strftime( '%G %T%z' ) ); + + $event = $editor->create_action_trigger_event( $event ); + + $client->respond( $event->id ); + } + + $editor->commit; + + return undef; +} +__PACKAGE__->register_method( + api_name => 'open-ils.trigger.event.autocreate', + method => 'create_events_for_object', + api_level=> 1, + stream => 1, + argc => 3 +); + + +sub fire_single_event { + my $self = shift; + my $client = shift; + my $event_id = shift; + + my $e = OpenILS::Application::Trigger::Event->new($event_id); + + if ($e->validate->valid) { + $e->react->cleanup; + } + + return { + valid => $e->valid, + reacted => $e->reacted, + cleanedup => $e->cleanedup, + event => $e->event + }; +} +__PACKAGE__->register_method( + api_name => 'open-ils.trigger.event.fire', + method => 'fire_single_event', + api_level=> 1, + argc => 1 +); + +sub fire_event_group { + my $self = shift; + my $client = shift; + my $events = shift; + + my $e = OpenILS::Application::Trigger::EventGroup->new(@$events); + + if ($e->validate->valid) { + $e->react->cleanup; + } + + return { + valid => $e->valid, + reacted => $e->reacted, + cleanedup => $e->cleanedup, + events => $e->events + }; +} +__PACKAGE__->register_method( + api_name => 'open-ils.trigger.event_group.fire', + method => 'fire_event_group', + api_level=> 1, + argc => 1 +); + +sub pending_events { + my $self = shift; + my $client = shift; + + my $editor = new_editor(); + + return $editor->search_action_trigger_event([ + { state => 'pending', run_time => {'<' => 'now'} }, + { idlist=> 1 } + ]); +} +__PACKAGE__->register_method( + api_name => 'open-ils.trigger.event.find_pending', + method => 'pending_events', + api_level=> 1 +); + + +sub grouped_events { + my $self = shift; + my $client = shift; + + my ($events) = $self->method_lookup('open-ils.trigger.event.find_pending')->run(); + + my %groups = ( '*' => [] ); + + for my $e_id ( @$events ) { + my $e = OpenILS::Application::Trigger::Event->new($e_id); + if ($e->validate->valid) { + if (my $group = $event->event->event_def->group_field) { + + # split the grouping link steps + my @steps = split '.', $group; + + # find the grouping object + my $node = $event->target; + $node = $node->$_() for ( @steps ); + + # get the pkey value for the grouping object on this event + my $node_ident = $node->Identity; + my $ident_value = $node->$node_ident(); + + # push this event onto the event+grouping_pkey_value stack + $groups{$e->event->event_def->id}{$ident_value} ||= []; + push @{ $groups{$e->event->event_def->id}{$ident_value} }, $e; + } else { + # it's a non-grouped event + push @{ $groups{'*'} }, $e; + } + } + } + + return \%groups; +} +__PACKAGE__->register_method( + api_name => 'open-ils.trigger.event.find_pending_by_group', + method => 'grouped_events', + api_level=> 1 +); + +sub run_all_events { + my $self = shift; + my $client = shift; + + my ($groups) = $self->method_lookup('open-ils.trigger.event.find_pending_by_group')->run(); + + for my $def ( %$groups ) { + if ($def eq '*') { + for my $event ( @{ $$groups{'*'} } ) { + $client->respond( + $self + ->method_lookup('open-ils.trigger.event.fire') + ->run($event) + ); + } + } else { + my $defgroup = $$groups{$def}; + for my $ident ( keys %$defgroup ) { + $client->respond( + $self + ->method_lookup('open-ils.trigger.event_group.fire') + ->run($$defgroup{$ident}) + ); + } + } + } + + +} +__PACKAGE__->register_method( + api_name => 'open-ils.trigger.event.run_all_pending', + method => 'run_all_events', + api_level=> 1 +); + + 1; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm b/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm index c2df13232f..2dc28f97eb 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm @@ -12,9 +12,15 @@ my $log = 'OpenSRF::Utils::Logger'; sub new { my $class = shift; my $id = shift; + my $editor = shift; $class = ref($class) || $class; - my $self = bless { id => $id, editor => new_editor() } => $class; + return $id if (ref($id) && ref($id) == $class); + + my $standalone = $editor ? 0 : 1; + $editor ||= new_editor(); + + my $self = bless { id => $id, editor => $editor, standalone => $standalone } => $class; return $self->init() } @@ -35,16 +41,41 @@ sub init { $self->id, { flesh => 2, flesh_fields => { - atev => [ 'event_def' ], - atevdef => [ 'hook' ] + atev => [ qw/event_def/ ], + atevdef => [ qw/hook env params/ ] } } ]) ); + if ($self->event->state eq 'valid') { + $self->valid(1); + } elsif ($self->event->state eq 'invalid') { + $self->valid(0); + } elsif ($self->event->state eq 'reacting') { + $self->valid(1); + } elsif ($self->event->state eq 'reacted') { + $self->valid(1); + $self->reacted(1); + } elsif ($self->event->state eq 'cleaning') { + $self->valid(1); + $self->reacted(1); + } elsif ($self->event->state eq 'complete') { + $self->valid(1); + $self->reacted(1); + $self->cleanedup(1); + } elsif ($self->event->state eq 'error') { + $self->valid(0); + $self->reacted(0); + $self->cleanedup(0); + } + + + $self->update_state('found') || die 'Unable to update event state'; + my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type ); - my $meth = "retreive_" . $class; + my $meth = "retrieve_" . $class; $meth =~ s/Fieldmapper:://; $meth =~ s/::/_/; @@ -55,6 +86,9 @@ sub init { sub cleanup { my $self = shift; + my $env = shift || $self->environment; + + return $self if (defined $self->cleanedup); if (defined $self->reacted) { $self->update_state( 'cleaning') || die 'Unable to update event state'; @@ -62,7 +96,7 @@ sub cleanup { my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure; $self->cleanedup( OpenILS::Application::Trigger::ModRunner::Cleanup - ->new( $cleanup, $self->environment ) + ->new( $cleanup, $env) ->run ->final_result ); @@ -85,6 +119,9 @@ sub cleanup { sub react { my $self = shift; + my $env = shift || $self->environment; + + return $self if (defined $self->reacted); if ($self->valid) { if ($self->event->event_def->group_field) { # can't react individually to a grouped definition @@ -94,7 +131,7 @@ sub react { try { $self->reacted( OpenILS::Application::Trigger::ModRunner::Reactor - ->new( $self->event->event_def->reactor, $self->environment ) + ->new( $self->event->event_def->reactor, $env ) ->run ->final_result ); @@ -213,6 +250,19 @@ sub editor { return $self->{editor}; } +sub unfind { + my $self = shift; + return undef unless (ref $self); + + die 'Cannot unfind a reacted event' if (defined $self->reacted); + + $self->update_state( 'pending' ) || die 'Unable to update event state'; + $self->{id} = undef; + $self->{event} = undef; + $self->{environment} = undef; + return $self; +} + sub target { my $self = shift; return undef unless (ref $self); @@ -222,6 +272,15 @@ sub target { return $self->{target}; } +sub standalone { + my $self = shift; + return undef unless (ref $self); + + my $t = shift; + $self->{standalone} = $t if (defined $t); + return $self->{standalone}; +} + sub update_state { my $self = shift; return undef unless ($self && ref $self); @@ -229,15 +288,35 @@ sub update_state { my $state = shift; return undef unless ($state); - $self->editor->xact_begin || return undef; + if ($self->standalone) { + $self->editor->xact_begin || return undef; + } my $e = $self->editor->retrieve_action_trigger_event( $self->id ); + $e->start_time( 'now' ) unless $e->start_time; $e->update_time( 'now' ); $e->update_process( $$ ); $e->state( $state ); - $self->editor->update_action_trigger_event( $e ); - return $self->editor->xact_commit || undef; + $e->clear_start_time() if ($e->state eq 'pending'); + + my $ok = $self->editor->update_action_trigger_event( $e ); + if (!$ok) { + $self->editor->xact_rollback if ($self->standalone); + return undef; + } else { + $ok = $self->editor->xact_commit if ($self->standalone); + } + + if ($ok) { + $e = $self->editor->data; + $self->event->start_time( $e->start_time ); + $self->event->update_time( $e->update_time ); + $self->event->update_process( $e->update_process ); + $self->event->state( $e->state ); + } + + return $ok || undef; } sub build_environment { @@ -251,19 +330,21 @@ sub build_environment { $self->environment->{target} = $self->target; $self->environment->{event} = $self->event; $self->environment->{template} = $self->event->event_def->template; + + $self->environment->{params}{ $_->param } = eval $_->value for ( @{$self->event->event_def->params} ); - my @env_list = $self->editor->search_action_trigger_environment( { event_def => $self->event->event_def } ); - my @param_list = $self->editor->search_action_trigger_params( { event_def => $self->event->event_def } ); - - $self->environment->{params}{ $_->param } = eval $_->value for ( @param_list ); - - for my $e ( @env_list ) { + for my $e ( @{$self->event->event_def->env} ) { my (@label, @path); @path = split('.', $e->path) if ($e->path); @label = split('.', $e->label) if ($e->label); $self->_object_by_path( $self->event->target, $e->collector, \@label, \@path ); } + + if ($self->event->event_def->group_field) { + my @group_path = split('.', $self->event->event_def->group_field); + my $group_object = $self->_object_by_path( $self->event->target, undef, [], \@group_path ); + } $self->environment->{complete} = 1; } otherwise { diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm b/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm new file mode 100644 index 0000000000..2c792e4bd2 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Trigger/EventGroup.pm @@ -0,0 +1,242 @@ +package OpenILS::Application::Trigger::EventGroup; +use OpenILS::Application::Trigger::Event; +use base 'OpenILS::Application::Trigger::Event'; +use OpenSRF::EX qw/:try/; + +use OpenSRF::Utils::Logger qw/:level/; + +use OpenILS::Utils::Fieldmapper; +use OpenILS::Utils::CStoreEditor q/:funcs/; +use OpenILS::Application::Trigger::ModRunner; + +my $log = 'OpenSRF::Utils::Logger'; + +sub new { + my $class = shift; + my @ids = @_; + $class = ref($class) || $class; + + my $editor = new_editor(xact=>1); + + my $self = bless { + environment => {}, + events => [ + map { + ref($_) ? + do { $_->standalone(0); $_->editor($editor); $_ } : + OpenILS::Application::Trigger::Event->new($_, $editor) + } @ids + ], + ids => \@ids, + editor => $editor + } => $class; + + + $self->editor->xact_commit; # flush out those updates + $self->editor->xact_begin; + + return $self; +} + +sub react { + my $self = shift; + + return $self if (defined $self->reacted); + + if ($self->valid) { + $self->update_state( 'reacting') || die 'Unable to update event group state'; + $self->build_environment; + + try { + $self->reacted( + OpenILS::Application::Trigger::ModRunner::Reactor + ->new( $self->event->event_def->reactor, $self->environment ) + ->run + ->final_result + ); + } otherwise { + $log->error( shift() ); + $self->update_state( 'error' ) || die 'Unable to update event group state'; + }; + + if (defined $self->reacted) { + $self->update_state( 'reacted' ) || die 'Unable to update event group state'; + } else { + $self->update_state( 'error' ) || die 'Unable to update event group state'; + } + } else { + $self->{reacted} = undef; + } + return $self; +} + +sub validate { + my $self = shift; + + return $self if (defined $self->valid); + + $self->update_state( 'validating') || die 'Unable to update event group state'; + $self->editor->xact_begin; + + my @valid_events; + try { + for my $event ( @{ $self->events } ) { + $event->validate; + push @valid_events, $event if ($event->valid); + } + $self->valid(1) if (@valid_events); + $self->{events} = \@valid_events; + $self->editor->xact_commit; + } otherwise { + $log->error( shift() ); + $self->editor->xact_rollback; + $self->update_state( 'error' ) || die 'Unable to update event group state'; + }; + + return $self; +} + +sub cleanedup { + my $self = shift; + return undef unless (ref $self); + + my $c = shift; + $self->{cleanedup} = $c if (defined $c); + return $self->{cleanedup}; +} + +sub reacted { + my $self = shift; + return undef unless (ref $self); + + my $r = shift; + $self->{reacted} = $r if (defined $r); + return $self->{reacted}; +} + +sub valid { + my $self = shift; + return undef unless (ref $self); + + my $v = shift; + $self->{valid} = $v if (defined $v); + return $self->{valid}; +} + +sub event { + my $self = shift; + return undef unless (ref $self); + + return $self->{events}[0]; +} + +sub events { + my $self = shift; + return undef unless (ref $self); + + return $self->{events}; +} + +sub ids { + my $self = shift; + return undef unless (ref $self); + + return $self->{ids}; +} + +sub environment { + my $self = shift; + return undef unless (ref $self); + + my $e = shift; + $self->{environment} = $e if (defined $e); + return $self->{environment}; +} + +sub editor { + my $self = shift; + return undef unless (ref $self); + + my $e = shift; + $self->{editor} = $e if (defined $e); + return $self->{editor}; +} + +sub unfind { + my $self = shift; + return undef unless (ref $self); + + die 'Cannot unfind a reacted event group' if (defined $self->reacted); + + $self->update_state( 'pending' ) || die 'Unable to update event group state'; + $self->{events} = undef; + return $self; +} + +sub update_state { + my $self = shift; + return undef unless ($self && ref $self); + + my $state = shift; + return undef unless ($state); + + $self->editor->xact_begin || return undef; + + my @oks; + for my $event ( @{ $self->events } ) { + my $e = $self->editor->retrieve_action_trigger_event( $event->id ); + $e->start_time( 'now' ) unless $e->start_time; + $e->update_time( 'now' ); + $e->update_process( $$ ); + $e->state( $state ); + + $e->clear_start_time() if ($e->state eq 'pending'); + + my $ok = $self->editor->update_action_trigger_event( $e ); + if ($ok) { + push @oks, $ok; + } + } + + if (scalar(@oks) < scalar(@{ $self->ids })) { + $self->editor->xact_rollback; + return undef; + } else { + $ok = $self->editor->xact_commit; + } + + if ($ok) { + for my $event ( @{ $self->events } ) { + my $updated = $self->editor->data; + $event->start_time( $updated->start_time ); + $event->update_time( $updated->update_time ); + $event->update_process( $updated->update_process ); + $event->state( $updated->state ); + } + } + + return $ok || undef; +} + +sub build_environment { + my $self = shift; + my $env = $self->environment; + + $$evn{target} = []; + $$evn{event} = []; + for my $e ( @{ $self->events } ) { + for my $evn_part ( keys %{ $e->environment } ) { + if ($env_part eq 'target') { + push @{ $$evn{target} }, $e->environment->{target}; + } elsif ($env_part eq 'event') { + push @{ $$evn{event} }, $e->environment->{event}; + } else { + $$evn{$evn_part} = $e->environment->{$evn_part}; + } + } + } + + return $self; +} + +1; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm b/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm index 3a6c58a526..c744ca4be5 100644 --- a/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm @@ -59,6 +59,7 @@ sub import { $$fieldmap{$n}{hint} = $c; $$fieldmap{$n}{virtual} = ($idl->{$c}{'oils_persist:virtual'} && $idl->{$c}{'oils_persist:virtual'} eq 'true') ? 1 : 0; $$fieldmap{$n}{table} = $idl->{$c}{'oils_persist:tablename'}; + $$fieldmap{$n}{restrict_primary} = $idl->{$c}{'oils_persist:restrict_primary'}; $$fieldmap{$n}{sequence} = $idl->{$c}{fields}{'oils_persist:sequence'}; $$fieldmap{$n}{identity} = $idl->{$c}{fields}{'oils_persist:primary'}; @@ -176,6 +177,11 @@ sub Identity { return $$fieldmap{$self->class_name}{identity}; } +sub RestrictPrimary { + my $self = shift; + return $$fieldmap{$self->class_name}{restrict_primary}; +} + sub Sequence { my $self = shift; return $$fieldmap{$self->class_name}{sequence}; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm index b2f52bc584..353e4e2d25 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Caption.pm @@ -3,6 +3,7 @@ use strict; use integer; use Carp; +use DateTime; use MARC::Record; sub new @@ -97,6 +98,8 @@ sub new sub decode_pattern { my $self = shift; my $pattern = $self->{PATTERN}->{y}; + + # XXX WRITE ME (?) } sub compressible { @@ -149,4 +152,215 @@ sub enumeration_is_chronology { return (exists $self->{PATTERN}->{w} && exists $self->{PATTERN}->{y}); } +my %daynames = ( + 'mo' => 1, + 'tu' => 2, + 'we' => 3, + 'th' => 4, + 'fr' => 5, + 'sa' => 6, + 'su' => 7, + ); + +my $daypat = '(mo|tu|we|th|fr|sa|su)'; +my $weekpat = '(99|98|97|00|01|02|03|04|05)'; +my $weeknopat; +my $monthpat = '(01|02|03|04|05|06|07|08|09|10|11|12)'; +my $seasonpat = '(21|22|23|24)'; + +# Initialize $weeknopat to be '(01|02|03|...|51|52|53)' +$weeknopat = '('; +foreach my $weekno (1..52) { + $weeknopat .= sprintf("%02d|", $weekno); +} +$weeknopat .= '53)'; + +sub match_day { + my $pat = shift; + my @date = @_; + # Translate daynames into day of week for DateTime + # also used to check if dayname is valid. + + if (exists $daynames{$pat}) { + # dd + # figure out day of week for date and compare + my $dt = DateTime->new(year => $date[0], + month => $date[1], + day => $date[2]); + return ($dt->day_of_week == $daynames{$pat}); + } elsif (length($pat) == 2) { + # MM + return $pat == $date[3]; + } elsif (length($pat) == 4) { + # MMDD + my ($mon, $day); + $mon = substr($pat, 0, 2); + $day = substr($pat, 2, 2); + + return (($mon == $date[1]) && ($day == $date[2])); + } else { + carp "Invalid day pattern '$pat'"; + return 0; + } +} + +# Calcuate date of "n"th last "dayname" of month: second last Tuesday +sub last_week_of_month { + my $dt = shift; + my $week = shift; + my $day = shift; + my $end_dt = DateTime->last_day_of_month(year => $dt->year, + month => $dt->month); + + $day = $daynames{$day}; + while ($end_dt->day_of_week != $day) { + $end_dt->subtract(days => 1); + } + + # 99: last week of month, 98: second last, etc. + for (my $i = 99 - $week; $i > 0; $i--) { + $end_dt->subtract(weeks => 1); + } + + return $end_dt; +} + +sub check_date { + my $dt = shift; + my $month = shift; + my $weekno = shift; + my $day = shift; + + if (!defined $day) { + # MMWW + return (($dt->month == $month) + && (($dt->week_of_month == $weekno) + || ($dt->week_of_month == last_day_of_month($dt, $weekno, 'th')->week_of_month))); + } + + # simple cases first + if ($daynames{$day} != $dt->day_of_week) { + # if it's the wrong day of the week, rest doesn't matter + return 0; + } + + if (!defined $month) { + # WWdd + return (($dt->weekday_of_month == $weekno) + || ($dt->weekday_of_month == last_day_of_month($dt, $weekno, $day)->weekday_of_month)); + } + + # MMWWdd + if ($month != $dt->month) { + # If it's the wrong month, then we're done + return 0; + } + + # It's the right day of the week + # It's the right month + + if ($weekno == $dt->weekday_of_month) { + # If this matches, then we're counting from the beginning + # of the month and it matches and we're done. + return 1; + } + + # only case left is that the week number is counting from + # the end of the month: eg, second last wednesday + return (last_week_of_month($weekno, $day)->weekday_of_month == $dt->weekday_of_month); +} + +sub match_week { + my $pat = shift; + my @date = @_; + my $dt = DateTime->new(year => $date[0], + month => $date[1], + day => $date[2]); + + if ($pat =~ m/^$weekpat$daypat$/) { + # WWdd: 03we = Third Wednesday + return check_date($dt, undef, $1, $2); + } elsif ($pat =~ m/^$monthpat$weekpat$daypat$/) { + # MMWWdd: 0599tu Last Tuesday in May XXX WRITE ME + return check_date($dt, $1, $2, $3); + } elsif ($pat =~ m/^$monthpat$weekpat$/) { + # MMWW: 1204: Fourth week in December XXX WRITE ME + return check_date($dt, $1, $2, undef); + } else { + carp "invalid week pattern '$pat'"; + return 0; + } +} + +sub match_month { + my $pat = shift; + my @date = @_; + + return ($pat eq $date[1]); +} + +sub match_season { + my $pat = shift; + my @date = @_; + + return ($pat eq $date[1]); +} + +sub match_year { + my $pat = shift; + my @date = @_; + + # XXX WRITE ME +} + +my %dispatch = ( + 'd' => \&match_day, + 'w' => \&match_week, + 'm' => \&match_month, + 's' => \&match_season, + 'y' => \&match_year, +); +sub regularity_match { + my $self = shift; + my $pubcode = shift; + my @date = @_; + + foreach my $regularity ($self->{PATTERN}->{y}) { + next unless $regularity =~ m/^$pubcode/; + + my $chroncode= substr($regularity, 1, 1); + my @pats = split(/,/, substr($regularity, 2)); + + # XXX WRITE ME + foreach my $pat (@pats) { + if ($dispatch{$chroncode}->($pat, @date)) { + return 1; + } + } + } + + return 0; +} + +sub is_omitted { + my $self = shift; + my @date = @_; + + return $self->regularity_match('o', @date); +} + +sub is_published { + my $self = shift; + my @date = @_; + + return $self->regularity_match('p', @date); +} + +sub is_combined { + my $self = shift; + my @date = @_; + + return $self->regularity_match('c', @date); +} + 1; diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm index 1d3405aa75..f5b37243db 100755 --- a/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm +++ b/Open-ILS/src/perlmods/OpenILS/Utils/MFHD/Holding.pm @@ -197,36 +197,20 @@ my %increments = { # x => completely irregular }; -sub next_date { - my $self = shift; - my $next = shift; - my @keys = @_; - my @cur; - my @new; - my $incr; - - my $caption = $self->{CAPTION}; - my $pattern = $caption->{PATTERN}; - my $frequency = $pattern->{w}; - - warn "I can't deal with publication patterns yet!" if exists $pattern->{y}; - -# print Dumper(@keys); -# print Dumper($self); +sub is_combined { + my $str = shift; - foreach my $i (0..@keys) { - $new[$i] = $cur[$i] = $self->{SUBFIELDS}->{$keys[$i]} - if exists $self->{SUBFIELDS}->{$keys[$i]}; - } + return $str =~ m;.+/.+; +} - if (defined $frequency) { - $incr = $increments{$frequency}; - } +sub incr_date { + my $incr = shift; + my @new = @_; - if (scalar(@cur) == 1) { + if (scalar(@new) == 1) { # only a year is specified. Next date is easy $new[0] += $incr->{years} || 1; - } elsif (scalar(@cur) == 2) { + } elsif (scalar(@new) == 2) { # Year and month or season if ($new[1] > 20) { # season @@ -245,7 +229,7 @@ sub next_date { $new[1] -= 12; } } - } elsif (scalar(@cur) == 3) { + } elsif (scalar(@new) == 3) { # Year, Month, Day: now it gets complicated. if ($new[2] =~ /^[0-9]+$/) { @@ -257,20 +241,47 @@ sub next_date { $new[0] = $dt->year; $new[1] = $dt->month; $new[2] = $dt->day; - } elsif ($new[2] =~ /^([0-9]+)\/([0-9]+)/) { - my $sdt = DateTime->new(year => $new[0], - month=> $new[1], - day => $1); - my $edt = DateTime->new(year => $new[0], - month=> $new[1], - day => $2); - $sdt->add(%{$incr}); - $edt->add(%{$incr}); - $new[0] = $sdt->year; - $new[1] = $sdt->month; - $new[2] = $sdt->day . '/' . $edt->day; - } else { - warn "I don't know how to deal with '$new[2]'"; + } + } else { + warn("Don't know how to cope with @new"); + } + + return @new; +} + +sub next_date { + my $self = shift; + my $next = shift; + my @keys = @_; + my @cur; + my @new; + my $incr; + + my $caption = $self->{CAPTION}; + my $reg = $caption->{REGULARITY}; + my $pattern = $caption->{PATTERN}; + my $freq = $pattern->{w}; + + foreach my $i (0..@keys) { + $new[$i] = $cur[$i] = $self->{SUBFIELDS}->{$keys[$i]} + if exists $self->{SUBFIELDS}->{$keys[$i]}; + } + + if (is_combined($new[-1])) { + $new[-1] =~ s/^[^\/]+//; + } + + # If $frequency is not one of the standard codes defined in %increments + # then there has to be a $yp publication regularity pattern that + # lists the dates of publication. Use that that list to find the next + # date following the current one. + # XXX: the code doesn't handle this case yet. + + if (defined $freq && exists $increments{$freq}) { + @new = incr_date($increments{$freq}, @new); + + while ($caption->is_omitted(@new)) { + @new = incr_date($increments{$freq}, @new); } } } diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql index 26ea81a757..cac02de327 100644 --- a/Open-ILS/src/sql/Pg/002.schema.config.sql +++ b/Open-ILS/src/sql/Pg/002.schema.config.sql @@ -476,7 +476,7 @@ CREATE TABLE config.z3950_source ( CREATE TABLE config.z3950_attr ( id SERIAL PRIMARY KEY, - source TEXT NOT NULL REFERENCES config.z3950_source (name), + source TEXT NOT NULL REFERENCES config.z3950_source (name) DEFERRABLE INITIALLY DEFERRED, name TEXT NOT NULL, label TEXT NOT NULL, code INT NOT NULL, diff --git a/Open-ILS/src/sql/Pg/005.schema.actors.sql b/Open-ILS/src/sql/Pg/005.schema.actors.sql index 6f014757ff..12e39d4553 100644 --- a/Open-ILS/src/sql/Pg/005.schema.actors.sql +++ b/Open-ILS/src/sql/Pg/005.schema.actors.sql @@ -456,6 +456,7 @@ CREATE TABLE actor.usr_address ( state TEXT NOT NULL, country TEXT NOT NULL, post_code TEXT NOT NULL, + pending BOOL NOT NULL DEFAULT FALSE, replaces INT REFERENCES actor.usr_address (id) DEFERRABLE INITIALLY DEFERRED ); diff --git a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql index dbdd1575c9..b9503e44a6 100644 --- a/Open-ILS/src/sql/Pg/012.schema.vandelay.sql +++ b/Open-ILS/src/sql/Pg/012.schema.vandelay.sql @@ -99,8 +99,8 @@ CREATE TABLE vandelay.bib_match ( -- DROP TABLE vandelay.import_item CASCADE; CREATE TABLE vandelay.import_item ( id BIGSERIAL PRIMARY KEY, - record BIGINT NOT NULL REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE, - definition BIGINT NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE, + record BIGINT NOT NULL REFERENCES vandelay.queued_bib_record (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + definition BIGINT NOT NULL REFERENCES vandelay.import_item_attr_definition (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, owning_lib INT, circ_lib INT, call_number TEXT, diff --git a/Open-ILS/src/sql/Pg/040.schema.asset.sql b/Open-ILS/src/sql/Pg/040.schema.asset.sql index 098792f03e..9b078132c6 100644 --- a/Open-ILS/src/sql/Pg/040.schema.asset.sql +++ b/Open-ILS/src/sql/Pg/040.schema.asset.sql @@ -69,8 +69,8 @@ CREATE RULE protect_copy_delete AS ON DELETE TO asset.copy DO INSTEAD UPDATE ass CREATE TABLE asset.copy_transparency ( id SERIAL PRIMARY KEY, deposit_amount NUMERIC(6,2), - owner INT NOT NULL REFERENCES actor.org_unit (id), - circ_lib INT REFERENCES actor.org_unit (id), + owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, + circ_lib INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, loan_duration INT CHECK ( loan_duration IN (1,2,3) ), fine_level INT CHECK ( fine_level IN (1,2,3) ), holdable BOOL, @@ -86,8 +86,8 @@ CREATE TABLE asset.copy_transparency ( CREATE TABLE asset.copy_tranparency_map ( id BIGSERIAL PRIMARY KEY, - tansparency INT NOT NULL REFERENCES asset.copy_transparency (id), - target_copy INT NOT NULL UNIQUE REFERENCES asset.copy (id) + tansparency INT NOT NULL REFERENCES asset.copy_transparency (id) DEFERRABLE INITIALLY DEFERRED, + target_copy INT NOT NULL UNIQUE REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED ); CREATE INDEX cp_tr_cp_idx ON asset.copy_tranparency_map (tansparency); diff --git a/Open-ILS/src/sql/Pg/070.schema.container.sql b/Open-ILS/src/sql/Pg/070.schema.container.sql index 910d12369f..f10545df82 100644 --- a/Open-ILS/src/sql/Pg/070.schema.container.sql +++ b/Open-ILS/src/sql/Pg/070.schema.container.sql @@ -34,12 +34,18 @@ CREATE TABLE container.copy_bucket ( DEFERRABLE INITIALLY DEFERRED, name TEXT NOT NULL, - btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.copy_bucket_type (code), + btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.copy_bucket_type (code) DEFERRABLE INITIALLY DEFERRED, pub BOOL NOT NULL DEFAULT FALSE, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), CONSTRAINT cb_name_once_per_owner UNIQUE (owner,name,btype) ); +CREATE TABLE container.copy_bucket_note ( + id SERIAL PRIMARY KEY, + bucket INT NOT NULL REFERENCES container.copy_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); + CREATE TABLE container.copy_bucket_item ( id SERIAL PRIMARY KEY, bucket INT NOT NULL @@ -54,9 +60,15 @@ CREATE TABLE container.copy_bucket_item ( ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + pos INT, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); +CREATE TABLE container.copy_bucket_item_note ( + id SERIAL PRIMARY KEY, + item INT NOT NULL REFERENCES container.copy_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); @@ -74,12 +86,18 @@ CREATE TABLE container.call_number_bucket ( DEFERRABLE INITIALLY DEFERRED, name TEXT NOT NULL, - btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.call_number_bucket_type (code), + btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.call_number_bucket_type (code) DEFERRABLE INITIALLY DEFERRED, pub BOOL NOT NULL DEFAULT FALSE, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), CONSTRAINT cnb_name_once_per_owner UNIQUE (owner,name,btype) ); +CREATE TABLE container.call_number_bucket_note ( + id SERIAL PRIMARY KEY, + bucket INT NOT NULL REFERENCES container.call_number_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); + CREATE TABLE container.call_number_bucket_item ( id SERIAL PRIMARY KEY, bucket INT NOT NULL @@ -94,9 +112,17 @@ CREATE TABLE container.call_number_bucket_item ( ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + pos INT, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); +CREATE TABLE container.call_number_bucket_item_note ( + id SERIAL PRIMARY KEY, + item INT NOT NULL REFERENCES container.call_number_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); + + CREATE TABLE container.biblio_record_entry_bucket_type ( @@ -114,12 +140,18 @@ CREATE TABLE container.biblio_record_entry_bucket ( DEFERRABLE INITIALLY DEFERRED, name TEXT NOT NULL, - btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.biblio_record_entry_bucket_type (code), + btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.biblio_record_entry_bucket_type (code) DEFERRABLE INITIALLY DEFERRED, pub BOOL NOT NULL DEFAULT FALSE, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), CONSTRAINT breb_name_once_per_owner UNIQUE (owner,name,btype) ); +CREATE TABLE container.biblio_record_entry_bucket_note ( + id SERIAL PRIMARY KEY, + bucket INT NOT NULL REFERENCES container.biblio_record_entry_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); + CREATE TABLE container.biblio_record_entry_bucket_item ( id SERIAL PRIMARY KEY, bucket INT NOT NULL @@ -134,9 +166,16 @@ CREATE TABLE container.biblio_record_entry_bucket_item ( ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + pos INT, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); +CREATE TABLE container.biblio_record_entry_bucket_item_note ( + id SERIAL PRIMARY KEY, + item INT NOT NULL REFERENCES container.biblio_record_entry_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); + CREATE TABLE container.user_bucket_type ( @@ -153,12 +192,18 @@ CREATE TABLE container.user_bucket ( DEFERRABLE INITIALLY DEFERRED, name TEXT NOT NULL, - btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.user_bucket_type (code), + btype TEXT NOT NULL DEFAULT 'misc' REFERENCES container.user_bucket_type (code) DEFERRABLE INITIALLY DEFERRED, pub BOOL NOT NULL DEFAULT FALSE, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), CONSTRAINT ub_name_once_per_owner UNIQUE (owner,name,btype) ); +CREATE TABLE container.user_bucket_note ( + id SERIAL PRIMARY KEY, + bucket INT NOT NULL REFERENCES container.user_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); + CREATE TABLE container.user_bucket_item ( id SERIAL PRIMARY KEY, bucket INT NOT NULL @@ -173,7 +218,15 @@ CREATE TABLE container.user_bucket_item ( ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + pos INT, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); +CREATE TABLE container.user_bucket_item_note ( + id SERIAL PRIMARY KEY, + item INT NOT NULL REFERENCES container.user_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, + note TEXT NOT NULL +); + + COMMIT; diff --git a/Open-ILS/src/sql/Pg/080.schema.money.sql b/Open-ILS/src/sql/Pg/080.schema.money.sql index f88a31852b..b0fc53f9d6 100644 --- a/Open-ILS/src/sql/Pg/080.schema.money.sql +++ b/Open-ILS/src/sql/Pg/080.schema.money.sql @@ -56,7 +56,7 @@ CREATE TABLE money.billing ( void_time TIMESTAMP WITH TIME ZONE, amount NUMERIC(6,2) NOT NULL, billing_type TEXT NOT NULL, - btype INT NOT NULL REFERENCES config.billing_type (id) ON DELETE RESTRICT, + btype INT NOT NULL REFERENCES config.billing_type (id) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED, note TEXT ); CREATE INDEX m_b_xact_idx ON money.billing (xact); diff --git a/Open-ILS/src/sql/Pg/090.schema.action.sql b/Open-ILS/src/sql/Pg/090.schema.action.sql index 7acb64a588..bc43428801 100644 --- a/Open-ILS/src/sql/Pg/090.schema.action.sql +++ b/Open-ILS/src/sql/Pg/090.schema.action.sql @@ -247,6 +247,17 @@ CREATE TRIGGER action_circulation_stop_fines_tgr FOR EACH ROW EXECUTE PROCEDURE action.circulation_claims_returned (); +CREATE TABLE action.hold_request_cancel_cause ( + id SERIAL PRIMARY KEY, + label TEXT UNIQUE +); +INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (1,'Untargeted expiration'); +INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (2,'Hold Shelf expiration'); +INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (3,'Patron via phone'); +INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (4,'Patron in person'); +INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (5,'Staff forced'); +INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (6,'Patron via OPAC'); +SELECT SETVAL('action.hold_request_cancel_cause_id_seq', 100); CREATE TABLE action.hold_request ( id SERIAL PRIMARY KEY, @@ -258,6 +269,8 @@ CREATE TABLE action.hold_request ( prev_check_time TIMESTAMP WITH TIME ZONE, expire_time TIMESTAMP WITH TIME ZONE, cancel_time TIMESTAMP WITH TIME ZONE, + cancel_cause INT REFERENCES action.hold_request_cancel_cause (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, + cancel_note TEXT, target BIGINT NOT NULL, -- see hold_type current_copy BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, fulfillment_staff INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, diff --git a/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql b/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql index 6820a9adb5..49ec817308 100644 --- a/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql +++ b/Open-ILS/src/sql/Pg/1.2.2.1-1.2.2.2-upgrade-db.sql @@ -19,7 +19,7 @@ BEGIN; CREATE SCHEMA extend_reporter; CREATE TABLE extend_reporter.legacy_circ_count ( - id BIGSERIAL PRIMARY KEY REFERENCES asset.copy (id), + id BIGSERIAL PRIMARY KEY REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED, circ_count INT NOT NULL DEFAULT 0 ); diff --git a/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql b/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql index 062cf706be..92066c691f 100644 --- a/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql +++ b/Open-ILS/src/sql/Pg/1.2.2.2-1.2.2.3-upgrade-db.sql @@ -20,7 +20,7 @@ CREATE SCHEMA extend_reporter; CREATE TABLE extend_reporter.legacy_circ_count ( - id BIGSERIAL PRIMARY KEY REFERENCES asset.copy (id), + id BIGSERIAL PRIMARY KEY REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED, circ_count INT NOT NULL DEFAULT 0 ); diff --git a/Open-ILS/src/sql/Pg/200.schema.acq.sql b/Open-ILS/src/sql/Pg/200.schema.acq.sql index 5926f1e3dd..773acab1a4 100644 --- a/Open-ILS/src/sql/Pg/200.schema.acq.sql +++ b/Open-ILS/src/sql/Pg/200.schema.acq.sql @@ -20,8 +20,8 @@ INSERT INTO acq.currency_type (code, label) VALUES ('EUR','Euros'); CREATE TABLE acq.exchange_rate ( id SERIAL PRIMARY KEY, - from_currency TEXT NOT NULL REFERENCES acq.currency_type (code), - to_currency TEXT NOT NULL REFERENCES acq.currency_type (code), + from_currency TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED, + to_currency TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED, ratio NUMERIC NOT NULL, CONSTRAINT exchange_rate_from_to_once UNIQUE (from_currency,to_currency) ); @@ -32,56 +32,94 @@ INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','E CREATE TABLE acq.provider ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, - owner INT NOT NULL REFERENCES actor.org_unit (id), - currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, + currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED, code TEXT UNIQUE, CONSTRAINT provider_name_once_per_owner UNIQUE (name,owner) ); +CREATE TABLE acq.provider_address ( + id SERIAL PRIMARY KEY, + valid BOOL NOT NULL DEFAULT TRUE, + address_type TEXT, + provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED, + street1 TEXT NOT NULL, + street2 TEXT, + city TEXT NOT NULL, + county TEXT, + state TEXT NOT NULL, + country TEXT NOT NULL, + post_code TEXT NOT NULL +); + +CREATE TABLE acq.provider_contact ( + id SERIAL PRIMARY KEY, + provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED, + name TEXT NULL NULL, + role TEXT, -- free-form.. e.g. "our sales guy" + email TEXT, + phone TEXT +); + +CREATE TABLE acq.provider_contact_address ( + id SERIAL PRIMARY KEY, + valid BOOL NOT NULL DEFAULT TRUE, + address_type TEXT, + contact INT NOT NULL REFERENCES acq.provider_contact (id) DEFERRABLE INITIALLY DEFERRED, + street1 TEXT NOT NULL, + street2 TEXT, + city TEXT NOT NULL, + county TEXT, + state TEXT NOT NULL, + country TEXT NOT NULL, + post_code TEXT NOT NULL +); + + CREATE TABLE acq.funding_source ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, - owner INT NOT NULL REFERENCES actor.org_unit (id), - currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, + currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED, code TEXT UNIQUE, CONSTRAINT funding_source_name_once_per_owner UNIQUE (name,owner) ); CREATE TABLE acq.funding_source_credit ( id SERIAL PRIMARY KEY, - funding_source INT NOT NULL REFERENCES acq.funding_source (id), + funding_source INT NOT NULL REFERENCES acq.funding_source (id) DEFERRABLE INITIALLY DEFERRED, amount NUMERIC NOT NULL, note TEXT ); CREATE TABLE acq.fund ( id SERIAL PRIMARY KEY, - org INT NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE, + org INT NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, name TEXT NOT NULL, year INT NOT NULL DEFAULT EXTRACT( YEAR FROM NOW() ), - currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED, code TEXT UNIQUE, CONSTRAINT name_once_per_org_year UNIQUE (org,name,year) ); CREATE TABLE acq.fund_debit ( id SERIAL PRIMARY KEY, - fund INT NOT NULL REFERENCES acq.fund (id), + fund INT NOT NULL REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED, origin_amount NUMERIC NOT NULL, -- pre-exchange-rate amount - origin_currency_type TEXT NOT NULL REFERENCES acq.currency_type (code), + origin_currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED, amount NUMERIC NOT NULL, encumbrance BOOL NOT NULL DEFAULT TRUE, debit_type TEXT NOT NULL, - xfer_destination INT REFERENCES acq.fund (id) + xfer_destination INT REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED ); CREATE TABLE acq.fund_allocation ( id SERIAL PRIMARY KEY, - funding_source INT NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE, - fund INT NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE, + funding_source INT NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + fund INT NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, amount NUMERIC, percent NUMERIC CHECK (percent IS NULL OR percent BETWEEN 0.0 AND 100.0), - allocator INT NOT NULL REFERENCES actor.usr (id), + allocator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, note TEXT, CONSTRAINT allocation_amount_or_percent CHECK ((percent IS NULL AND amount IS NOT NULL) OR (percent IS NOT NULL AND amount IS NULL)) ); @@ -89,8 +127,8 @@ CREATE TABLE acq.fund_allocation ( CREATE TABLE acq.picklist ( id SERIAL PRIMARY KEY, - owner INT NOT NULL REFERENCES actor.usr (id), - org_unit INT NOT NULL REFERENCES actor.org_unit (id), + owner INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, + org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, name TEXT NOT NULL, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), @@ -99,11 +137,11 @@ CREATE TABLE acq.picklist ( CREATE TABLE acq.purchase_order ( id SERIAL PRIMARY KEY, - owner INT NOT NULL REFERENCES actor.usr (id), - ordering_agency INT NOT NULL REFERENCES actor.org_unit (id), + owner INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, + ordering_agency INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), - provider INT NOT NULL REFERENCES acq.provider (id), + provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED, state TEXT NOT NULL DEFAULT 'new' ); CREATE INDEX po_owner_idx ON acq.purchase_order (owner); @@ -112,9 +150,9 @@ CREATE INDEX po_state_idx ON acq.purchase_order (state); CREATE TABLE acq.po_note ( id SERIAL PRIMARY KEY, - purchase_order INT NOT NULL REFERENCES acq.purchase_order (id), - creator INT NOT NULL REFERENCES actor.usr (id), - editor INT NOT NULL REFERENCES actor.usr (id), + purchase_order INT NOT NULL REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED, + creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, + editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), value TEXT NOT NULL @@ -123,15 +161,15 @@ CREATE INDEX po_note_po_idx ON acq.po_note (purchase_order); CREATE TABLE acq.lineitem ( id BIGSERIAL PRIMARY KEY, - selector INT NOT NULL REFERENCES actor.org_unit (id), - provider INT REFERENCES acq.provider (id), - purchase_order INT REFERENCES acq.purchase_order (id), - picklist INT REFERENCES acq.picklist (id), + selector INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, + provider INT REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED, + purchase_order INT REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED, + picklist INT REFERENCES acq.picklist (id) DEFERRABLE INITIALLY DEFERRED, expected_recv_time TIMESTAMP WITH TIME ZONE, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), marc TEXT NOT NULL, - eg_bib_id INT REFERENCES biblio.record_entry (id), + eg_bib_id INT REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED, source_label TEXT, item_count INT NOT NULL DEFAULT 0, state TEXT NOT NULL DEFAULT 'new', @@ -142,9 +180,9 @@ CREATE INDEX li_pl_idx ON acq.lineitem (picklist); CREATE TABLE acq.lineitem_note ( id SERIAL PRIMARY KEY, - lineitem INT NOT NULL REFERENCES acq.lineitem (id), - creator INT NOT NULL REFERENCES actor.usr (id), - editor INT NOT NULL REFERENCES actor.usr (id), + lineitem INT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED, + creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, + editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED, create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), value TEXT NOT NULL @@ -153,14 +191,14 @@ CREATE INDEX li_note_li_idx ON acq.lineitem_note (lineitem); CREATE TABLE acq.lineitem_detail ( id BIGSERIAL PRIMARY KEY, - lineitem INT NOT NULL REFERENCES acq.lineitem (id), - fund INT REFERENCES acq.fund (id), - fund_debit INT REFERENCES acq.fund_debit (id), - eg_copy_id BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL, + lineitem INT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED, + fund INT REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED, + fund_debit INT REFERENCES acq.fund_debit (id) DEFERRABLE INITIALLY DEFERRED, + eg_copy_id BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, barcode TEXT, cn_label TEXT, - owning_lib INT REFERENCES actor.org_unit (id) ON DELETE SET NULL, - location INT REFERENCES asset.copy_location (id) ON DELETE SET NULL, + owning_lib INT REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, + location INT REFERENCES asset.copy_location (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, recv_time TIMESTAMP WITH TIME ZONE ); @@ -182,7 +220,7 @@ CREATE TABLE acq.lineitem_marc_attr_definition ( CREATE TABLE acq.lineitem_provider_attr_definition ( id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'), xpath TEXT NOT NULL, - provider INT NOT NULL REFERENCES acq.provider (id) + provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED ) INHERITS (acq.lineitem_attr_definition); CREATE TABLE acq.lineitem_generated_attr_definition ( @@ -192,7 +230,7 @@ CREATE TABLE acq.lineitem_generated_attr_definition ( CREATE TABLE acq.lineitem_usr_attr_definition ( id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'), - usr INT NOT NULL REFERENCES actor.usr (id) + usr INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED ) INHERITS (acq.lineitem_attr_definition); CREATE TABLE acq.lineitem_local_attr_definition ( @@ -202,7 +240,7 @@ CREATE TABLE acq.lineitem_local_attr_definition ( CREATE TABLE acq.lineitem_attr ( id BIGSERIAL PRIMARY KEY, definition BIGINT NOT NULL, - lineitem BIGINT NOT NULL REFERENCES acq.lineitem (id), + lineitem BIGINT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED, attr_type TEXT NOT NULL, attr_name TEXT NOT NULL, attr_value TEXT NOT NULL diff --git a/Open-ILS/src/sql/Pg/210.schema.serials.sql b/Open-ILS/src/sql/Pg/210.schema.serials.sql index 56a4391760..9637dada77 100644 --- a/Open-ILS/src/sql/Pg/210.schema.serials.sql +++ b/Open-ILS/src/sql/Pg/210.schema.serials.sql @@ -10,7 +10,7 @@ CREATE TABLE asset.uri ( active BOOL NOT NULL DEFAULT TRUE ); -ALTER TABLE asset.call_number ADD COLUMN uri INT REFERENCES asset.uri (id); +ALTER TABLE asset.call_number ADD COLUMN uri INT REFERENCES asset.uri (id) DEFERRABLE INITIALLY DEFERRED; BEGIN; diff --git a/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql b/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql index 8911ed9566..f86e9752d6 100644 --- a/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql +++ b/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql @@ -87,12 +87,12 @@ INSERT INTO action_trigger.cleanup (module,description) VALUES ('ClearAllPending CREATE TABLE action_trigger.event_definition ( id SERIAL PRIMARY KEY, active BOOL NOT NULL DEFAULT TRUE, - owner INT NOT NULL REFERENCES actor.org_unit (id), - hook TEXT NOT NULL REFERENCES action_trigger.hook (key), - validator TEXT NOT NULL REFERENCES action_trigger.validator (module), - reactor TEXT NOT NULL REFERENCES action_trigger.reactor (module), - cleanup_success TEXT REFERENCES action_trigger.cleanup (module), - cleanup_failure TEXT REFERENCES action_trigger.cleanup (module), + owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, + hook TEXT NOT NULL REFERENCES action_trigger.hook (key) DEFERRABLE INITIALLY DEFERRED, + validator TEXT NOT NULL REFERENCES action_trigger.validator (module) DEFERRABLE INITIALLY DEFERRED, + reactor TEXT NOT NULL REFERENCES action_trigger.reactor (module) DEFERRABLE INITIALLY DEFERRED, + cleanup_success TEXT REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED, + cleanup_failure TEXT REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED, delay INTERVAL NOT NULL DEFAULT '5 minutes', delay_field TEXT, -- for instance, xact_start on a circ hook ... look for fields on hook.core_type where datatype=timestamp? If not set, delay from now() group_field TEXT, -- field from this.hook.core_type to batch event targets together on, fed into reactor a group at a time. @@ -102,11 +102,11 @@ CREATE TABLE action_trigger.event_definition ( CREATE TABLE action_trigger.environment ( id SERIAL PRIMARY KEY, - event_def INT NOT NULL REFERENCES action_trigger.event_definition (id), + event_def INT NOT NULL REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED, path TEXT, -- fields to flesh. given a hook with a core_type of circ, imagine circ_lib.parent_ou expanding to -- {flesh: 2, flesh_fields: {circ: ['circ_lib'], aou: ['parent_ou']}} ... default is to flesh all -- at flesh depth 1 - collector TEXT REFERENCES action_trigger.collector (module), -- if set, given the object at 'path', return some data + collector TEXT REFERENCES action_trigger.collector (module) DEFERRABLE INITIALLY DEFERRED, -- if set, given the object at 'path', return some data -- to be stashed at environment.